diff --git a/.github/workflows/rundoc-ci.yml b/.github/workflows/rundoc-ci.yml index ae2e18c..37ad9e7 100644 --- a/.github/workflows/rundoc-ci.yml +++ b/.github/workflows/rundoc-ci.yml @@ -6,6 +6,12 @@ on: jobs: test-rundoc: runs-on: ubuntu-latest + env: + BUNDLE_GEMFILE: .rundoc-workspace/Gemfile + strategy: + matrix: + lang: ["ruby", "dotnet", "nodejs", "python", "php", "go", "java_maven", "java_gradle", "scala"] + fail-fast: false steps: - uses: actions/checkout@v4 with: @@ -15,9 +21,11 @@ jobs: with: ruby-version: "3.3" bundler-cache: true # do run 'bundle install' and cache - working-directory: .rundoc-workspace - uses: buildpacks/github-actions/setup-pack@v5.8.3 # [CONFIG] add different languages set up here - - run: bundle exec rundoc ../docs/src/ruby/ruby_tutorial.md --on-success-dir ../docs/ruby --on-failure-dir ../docs/fail --force - working-directory: .rundoc-workspace + - run: | + bundle exec rundoc docs/src/${{ matrix.lang }}/RUNDOC.md \ + --on-success-dir docs/${{ matrix.lang }} \ + --on-failure-dir docs/fail/${{ matrix.lang }} \ + --force diff --git a/.github/workflows/rundoc-pr.yml b/.github/workflows/rundoc-pr.yml index bc3519c..6df098a 100644 --- a/.github/workflows/rundoc-pr.yml +++ b/.github/workflows/rundoc-pr.yml @@ -8,6 +8,12 @@ on: jobs: run-rundoc: runs-on: pub-hk-ubuntu-24.04-ip + env: + BUNDLE_GEMFILE: .rundoc-workspace/Gemfile + strategy: + matrix: + lang: ["ruby", "dotnet", "nodejs", "python", "php", "go", "java_maven", "java_gradle", "scala"] + fail-fast: false steps: - name: Get token for GH application (Linguist) uses: actions/create-github-app-token@v1 @@ -25,23 +31,21 @@ jobs: with: ruby-version: "3.3" bundler-cache: true # do run 'bundle install' and cache - working-directory: .rundoc-workspace - uses: buildpacks/github-actions/setup-pack@v5.8.3 # [CONFIG] add different languages set up here - - run: bundle exec rundoc ../docs/src/ruby/ruby_tutorial.md --on-success-dir ../docs/ruby --on-failure-dir ../docs/fail --force - working-directory: .rundoc-workspace + - run: | + bundle exec rundoc docs/src/${{ matrix.lang }}/RUNDOC.md \ + --on-success-dir docs/${{ matrix.lang }} \ + --on-failure-dir docs/fail/${{ matrix.lang }} \ + --force continue-on-error: true - - run: rm -rf ruby-getting-started - working-directory: docs/ruby - continue-on-error: true - - name: Create Pull Request id: pr uses: peter-evans/create-pull-request@v7 with: token: ${{ steps.generate-token.outputs.token }} - branch: rundoc-update + branch: rundoc-update-${{ matrix.lang }} delete-branch: true base: main commit-message: Update tutorials diff --git a/.rundoc-workspace/Gemfile.lock b/.rundoc-workspace/Gemfile.lock index 6a335f1..10043ba 100644 --- a/.rundoc-workspace/Gemfile.lock +++ b/.rundoc-workspace/Gemfile.lock @@ -4,7 +4,7 @@ GEM addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) aws-eventstream (1.3.0) - aws-partitions (1.1014.0) + aws-partitions (1.1022.0) aws-sdk-core (3.214.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) @@ -13,7 +13,7 @@ GEM aws-sdk-kms (1.96.0) aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.174.0) + aws-sdk-s3 (1.176.1) aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) @@ -29,22 +29,22 @@ GEM rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - dotenv (3.1.4) + dotenv (3.1.6) jmespath (1.6.2) - logger (1.6.1) + logger (1.6.3) matrix (0.4.2) mini_mime (1.1.5) - nokogiri (1.16.7-aarch64-linux) + nokogiri (1.17.2-aarch64-linux) racc (~> 1.4) - nokogiri (1.16.7-arm-linux) + nokogiri (1.17.2-arm-linux) racc (~> 1.4) - nokogiri (1.16.7-arm64-darwin) + nokogiri (1.17.2-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.7-x86-linux) + nokogiri (1.17.2-x86-linux) racc (~> 1.4) - nokogiri (1.16.7-x86_64-darwin) + nokogiri (1.17.2-x86_64-darwin) racc (~> 1.4) - nokogiri (1.16.7-x86_64-linux) + nokogiri (1.17.2-x86_64-linux) racc (~> 1.4) parslet (2.0.0) public_suffix (6.0.1) @@ -55,7 +55,7 @@ GEM regexp_parser (2.9.3) rexml (3.3.9) rubyzip (2.3.2) - rundoc (4.0.0) + rundoc (4.1.1) aws-sdk-s3 (~> 1) base64 (~> 0) capybara (~> 3) diff --git a/README.md b/README.md index 74c913e..24bf0a7 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,22 @@ Heroku Cloud Native Buildpacks Logo

-Create a production-ready container image for your application with a [single command](TUTORIAL.md); no Dockerfile required. [Try Heroku Cloud Native Buildpacks today](TUTORIAL.md). +Create a production-ready container image for your application with a single command; no Dockerfile required. Try Heroku Cloud Native Buildpacks today with one of the local language-specific tutorials: + +- [.NET](docs/dotnet/README.md) +- [Go](docs/go/README.md) +- [Java (Gradle)](docs/java_gradle/README.md) +- [Java (Maven)](docs/java_maven/README.md) +- [Node.JS](docs/nodejs/README.md) +- [PHP](docs/php/README.md) +- [Python](docs/python/README.md) +- [Ruby](docs/ruby/README.md) +- [Scala](docs/scala/README.md) > [!IMPORTANT] -> The Heroku Cloud Native Buildpacks program is an open-source preview, and is -not currently available for use on the Heroku platform. If you are looking for -information or help with Heroku Cedar Buildpacks on the Heroku platform, you -may find more information [here](https://devcenter.heroku.com/articles/buildpacks). +> The Heroku Cloud Native Buildpacks are available for use on [Heroku Fir](https://devcenter.heroku.com/articles/generations#fir) generation. If you are looking for +> information or help with Heroku Cedar generation [Classic Buildpacks](https://devcenter.heroku.com/articles/buildpacks#classic-buildpacks) on the Heroku platform, you +> may find more information [on the Dev Center](https://devcenter.heroku.com/articles/buildpacks). ## What is a Cloud Native Buildpack? @@ -19,7 +28,7 @@ Cloud Native Buildpacks (CNBs) are an [open specification for building and runni ### Philosophy -Heroku CNBs are written with the app developer in mind. We want it to be as easy and simple as possible to transform an app into an OCI image. While a Dockerfile is procedural, CNBs are declarative. Give us your application, and we'll give you an image that can run it. Don't believe us? [Try it out today](TUTORIAL.md). +Heroku CNBs are written with the app developer in mind. We want it to be as easy and simple as possible to transform an app into an OCI image. While a Dockerfile is procedural, CNBs are declarative. Give us your application, and we'll give you an image that can run it. Don't believe us? [Try it out today](docs/README.md). We believe: - Building OCI images shouldn't be a pain. @@ -59,7 +68,7 @@ To run our buildpack locally, we will use `pack`, a tool maintained by the Cloud ### Create the app image > [!NOTE] -> For a full hands-on getting started experience [follow our getting started tutorial](TUTORIAL.md). +> For a full hands-on getting started experience [follow our getting started tutorials](docs/README.md). First, we need an application to produce an image. We will use [Heroku's getting started app for Node.js](https://github.com/heroku/node-js-getting-started), but any other app that uses a supported ecosystem will work. Clone the application: @@ -73,7 +82,7 @@ In your application directory, run the `pack build` command. No additional steps $ pack build my-app-image --builder heroku/builder:24 --path node-js-getting-started ``` -Once the image is built, you can run it with the tools you're already comfortable with, like the `docker` CLI, for a demonstration of how [read out getting started tutorial](TUTORIAL.md). +Once the image is built, you can run it with the tools you're already comfortable with, like the `docker` CLI, for a demonstration of how [read out getting started tutorials](docs/README.md). ## Discussions diff --git a/TUTORIAL.md b/TUTORIAL.md index 23b2984..9b6505b 100644 --- a/TUTORIAL.md +++ b/TUTORIAL.md @@ -1,364 +1,3 @@ -# Cloud Native Buildpack Tutorial -Build a Node.js application image in 5 minutes, no Dockerfile required. +# Tutorials (moved) -At the end of this tutorial, you'll have a working [OCI image](https://opencontainers.org/) of a Node.js application that can run locally. You will learn about the Cloud Native Buildpack (CNB) ecosystem, and how to utilize the [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/) to build images without the need to write or maintain a Dockerfile. - -## Install the pack CLI - -We assume you have [docker installed](https://docs.docker.com/engine/install/) and a working copy [of git](https://github.com/git-guides/install-git). Next, you will need to install the CLI tool for building CNBs, [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/). If you're on a Mac you can install it via Homebrew: - -``` -$ brew install buildpacks/tap/pack -``` - -Ensure that it is installed correctly: - -``` -$ pack --version -0.35.0+git-f6b450f.build-6065 -``` - -## Configure the default pack builder - -Once `pack` is installed, the only configuration you'll need for this tutorial is to set a default builder: - -``` -$ pack config default-builder heroku/builder:24 -Builder heroku/builder:24 is now the default builder -``` - -You can view your default builder at any time: - -``` -$ pack config default-builder -The current default builder is heroku/builder:24 -``` - -## What is a builder? - -> [!NOTE] -> Skip ahead if you want to build the application first and get into the details later. You won't need to -> know about builders for the rest of this tutorial. - -In short, a builder is a delivery mechanism for buildpacks. A builder contains references to base images and individual buildpacks. A base image contains the operating system and system dependencies. Buildpacks are the components that will configure an image to run your application, that’s where the bulk of the logic lives and why the project is called “Cloud Native Buildpacks” and not “Cloud Native Builders.” - -You can view the contents of a builder via the command `pack builder inspect`. For example: - -``` -$ pack builder inspect heroku/builder:24 -# ... -Buildpacks: - ID NAME VERSION HOMEPAGE - heroku/go Heroku Go 0.4.1 https://github.com/heroku/buildpacks-go - heroku/gradle Heroku Gradle 6.0.1 https://github.com/heroku/buildpacks-jvm - heroku/java Heroku Java 6.0.1 https://github.com/heroku/buildpacks-jvm - heroku/jvm Heroku OpenJDK 6.0.1 https://github.com/heroku/buildpacks-jvm - heroku/maven Heroku Maven 6.0.1 https://github.com/heroku/buildpacks-jvm - heroku/nodejs Heroku Node.js 3.2.9 https://github.com/heroku/buildpacks-nodejs - heroku/nodejs-corepack Heroku Node.js Corepack 3.2.9 https://github.com/heroku/buildpacks-nodejs - heroku/nodejs-engine Heroku Node.js Engine 3.2.9 https://github.com/heroku/buildpacks-nodejs - heroku/nodejs-npm-engine Heroku Node.js npm Engine 3.2.9 https://github.com/heroku/buildpacks-nodejs -# ... -``` - -> [!NOTE] -> Your output version numbers may differ. - -This output shows the various buildpacks that represent the different languages that are supported by this builder such as `heroku/go` and `heroku/nodejs-engine`. - -## Download an example Node.js application - -How do you configure a CNB? Give them an application. While Dockerfile is procedural, buildpacks, are declarative. A buildpack will determine what your application needs to function by inspecting the code on disk. - -For this example, we're using a pre-built Node.js application. Download it now: - -``` -$ git clone https://github.com/heroku/node-js-getting-started -$ cd node-js-getting-started -``` - -Verify you're in the correct directory: - -``` -$ ls -1 -Procfile -README.md -app.json -index.js -package-lock.json -package.json -public -test.js -views -``` - -Now build an image named `my-image-name` by executing the `heroku/builder:24` against the application by running the -`pack build` command: - -``` -$ pack build my-image-name --path . -24: Pulling from heroku/builder -Digest: sha256:166781425a049e081a3fd23882905a7e35b5d79d57b18028cad18142a739281b -Status: Image is up to date for heroku/builder:24 -24: Pulling from heroku/heroku -Digest: sha256:36e92b6b4e2a980a02363b9f1ddce582c32f38e236b778a6570dedf3d52ab733 -Status: Image is up to date for heroku/heroku:24 -===> ANALYZING -Image with name "my-image-name" not found -===> DETECTING -3 of 5 buildpacks participating -heroku/nodejs-engine 3.2.9 -heroku/nodejs-npm-install 3.2.9 -heroku/procfile 3.1.2 -===> RESTORING -Restoring metadata for "heroku/nodejs-npm-install:npm_cache" from cache -Restoring data for "heroku/nodejs-npm-install:npm_cache" from cache -===> BUILDING - -[Heroku Node.js Engine Buildpack] - -[Checking Node.js version] -Detected Node.js version range: >=20.0.0 <21.0.0-0 -Resolved Node.js version: 20.15.1 - -# ... -- Done (finished in 0.744s) - -[Discovering process types] -Procfile declares types -> web -===> EXPORTING -Adding layer 'heroku/nodejs-engine:dist' -Adding layer 'heroku/nodejs-engine:node_runtime_metrics' -Adding layer 'heroku/nodejs-engine:web_env' -Adding layer 'heroku/nodejs-npm-install:npm_runtime_config' -Adding layer 'buildpacksio/lifecycle:launch.sbom' -Adding 1/1 app layer(s) -Adding layer 'buildpacksio/lifecycle:launcher' -Adding layer 'buildpacksio/lifecycle:config' -Adding layer 'buildpacksio/lifecycle:process-types' -Adding label 'io.buildpacks.lifecycle.metadata' -Adding label 'io.buildpacks.build.metadata' -Adding label 'io.buildpacks.project.metadata' -Setting default process type 'web' -Saving my-image-name... -*** Images (d4ce4d02bd1c): - my-image-name -Reusing cache layer 'heroku/nodejs-engine:dist' -Adding cache layer 'heroku/nodejs-engine:dist' -Adding cache layer 'heroku/nodejs-npm-install:npm_cache' -Successfully built image my-image-name -``` - -> [!NOTE] -> Your output may differ. - -Verify that you see “Successfully built image my-image-name” at the end of the output. And verify that the image is present locally: - -``` -$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" | grep my-image-name -d4ce4d02bd1c my-image-name latest -``` - -## What does `pack build` do? - -> [!NOTE] -> Skip ahead if you want to run the application first and get into the details later. - -When you run `pack build` with a builder, each buildpack runs a detection script to determine if it should be eligible to build the application. In our case the `heroku/nodejs-engine` and `heroku/nodejs-npm-install` buildpacks found a `package.json` file on disk and reported that they knew how to install those dependencies. You can view a list of the buildpacks used in the output above: - -``` -===> DETECTING -3 of 5 buildpacks participating -heroku/nodejs-engine 3.2.9 -heroku/nodejs-npm-install 3.2.9 -heroku/procfile 3.1.2 -``` - -After the detect phase, each buildpack will execute. Buildpacks can inspect your project, install files to disk, run commands, write environment variables, [and more](https://buildpacks.io/docs/for-buildpack-authors/). You can see some examples of that in the output above. - -You can see that the Node binary is being downloaded: - -``` -[Installing Node.js distribution] -``` - -Later you can see npm dependencies installed: - -``` - - Running `npm ci "--production=false"` - - npm warn config production Use `--omit=dev` instead. - - added 161 packages, and audited 162 packages in 606ms - - 75 packages are looking for funding - run `npm fund` for details - - found 0 vulnerabilities - - - Done (0.638s) -``` - -If you’re familiar with Dockerfile you might know that [many commands in a Dockerfile will create a layer](https://dockerlabs.collabnix.com/beginners/dockerfile/Layering-Dockerfile.html). Buildpacks also use layers, but the CNB buildpack API provides for fine grained control over what exactly is in these layers and how they’re composed. Unlike Dockerfile, all images produced by CNBs [can be rebased](https://tag-env-sustainability.cncf.io/blog/2023-12-reduce-reuse-rebase-buildpacks/#reduce-reuserebase). The CNB api also improves on many of the pitfalls outlined in the satirical article [Write a Good Dockerfile in 19 'Easy' Steps](https://jkutner.github.io/2021/04/26/write-good-dockerfile.html). - -## Use the image - -Even though we used `pack` and CNBs to build our image, it can be run with your favorite tools like any other OCI image. We will be using the `docker` command line to run our image. - -By default, images will be booted into a web server configuration. You can launch the app we just built by running: - -``` -$ docker run -it --rm --env PORT=9292 -p 9292:9292 my-image-name -``` - -Now when you visit http://localhost:9292 you should see a working web application: - -![Screenshot of Heroku's Node.js getting started app](nodejs-getting-started-screenshot.png) - -Here's a quick breakdown of that command we just ran: - -- `docker run` Create and run a new container from an image. -- `-it` Makes the container interactive and allocates a TTY. -- `--rm` Automatically remove the container when it exits. -- `--env PORT=9292` Creates an environment variable named `PORT` and sets it to `9292` this is needed so the application inside the container knows what port to bind the web server. -- `-p 9292:9292` Publishes a container's port(s) to the host. This is what allows requests from your machine to be received by the container. -- `my-image-name` The name of the image you want to use for the application. - -So far, we've downloaded an application via git and run a single command `pack build` to generate an image, and then we can use that image as if it was generated via a Dockerfile via the `docker run` command. - -In addition to running the image as a web server, you can access the container's terminal interactively. In a new terminal window try running this command: - -``` -$ docker run -it --rm my-image-name bash -``` - -Now you can inspect the container interactively. For example, you can see the npm version: - -``` -heroku@d97cd3f44535:/workspace$ npm --version -10.7.0 -``` - -View files on disk: - -``` -heroku@3f4c2ebedff9:/workspace$ ls -Procfile app.json node_modules package.json test.js -README.md index.js package-lock.json public views -heroku@3f4c2ebedff9:/workspace$ cat Procfile -web: npm start -``` - -And anything else you would typically do via an interactive container session. - -## Image structure under the hood - -> [!NOTE] -> Skip this section if you want to try building your application with CNBs and learn about container structure later. - -If you’re an advanced `Dockerfile` user you might be interested in learning more about the internal structure of the image on disk. You can access the image disk interactively by using the `bash` docker command above. - -If you view the root directory `/` you’ll see there is a `layers` folder. Every buildpack that executes gets a unique folder. For example: - -``` -heroku@edc80579a982:/workspace$ ls /layers | grep node -heroku_nodejs-engine -heroku_nodejs-npm-install -``` - -Individual buildpacks can compose multiple layers from their buildpack directory. For example you can see that `npm` is present within that node buildpack directory under a layer named `dist`: - -``` -heroku@edc80579a982:/workspace$ which npm -/layers/heroku_nodejs-engine/dist/bin/npm -``` - -OCI images are represented as sequential modifications to disk. By scoping buildpack disk modifications to their own directory, the CNB API guarantees that changes to a layer in one buildpack will not affect the contents of disk to another layer. This means that OCI images produced by CNBs are rebaseable by default, while those produced by Dockerfile are not. - -We saw before how the image booted a web server by default. This is accomplished using an entrypoint. In another terminal outside of the running container you can view that entrypoint: - -``` -$ docker inspect my-image-name | grep '"Entrypoint": \[' -A2 - "Entrypoint": [ - "/cnb/process/web" - ], -``` - -From within the image, you can see that file on disk: - -``` -heroku@edc80579a982:/workspace$ ls /cnb/process/ -web -``` - -While you might not need this level of detail to build and run an application with Cloud Native Buildpacks, it is useful to understand how they’re structured if you ever want to write your own buildpack. - -## Try CNBs out on your application - -So far we've learned that CNBs are a declarative interface for producing OCI images (like docker). They aim to be no to low configuration and once built, you can interact with them like any other image. - -For the next step, we encourage you to try running `pack` with the Heroku builder against your application and let us know how it went. We encourage you to share your experience by [opening a discussion](https://github.com/heroku/buildpacks/discussions) and walking us through what happened: - -- What went well? -- What could be better? -- Do you have any questions? - -We are actively working on our Cloud Native Buildpacks and want to hear about your experience. The documentation below covers some intermediate-level topics that you might find helpful. - -## Configuring multiple languages - -Language support is provided by individual buildpacks that are shipped with the builder. The above example uses the `heroku/nodejs` buildpack which is [visible on GitHub](https://github.com/heroku/buildpacks-nodejs). When you execute `pack build` with a builder, every buildpack has the opportunity to "detect" if it should execute against that project. The `heroku/nodejs` buildpack looks for a `package.json` in the root of the project and if found, knows how to detect a node version and install dependencies. - -In addition to this auto-detection behavior, you can specify buildpacks through the `--buildpack` flag with the `pack` CLI or through a [project.toml](https://buildpacks.io/docs/for-app-developers/how-to/build-inputs/specify-buildpacks/) file at the root of your application. - -For example, if you wanted to install both Node.js and Python you could create a `project.toml` file in the root of your application: - -```toml -[_] -schema-version = "0.2" -id = "sample.nodejs+python.app" -name = "Sample Nodejs & Python App" -version = "1.0.0" - -[[io.buildpacks.group]] -uri = "heroku/python" - -[[io.buildpacks.group]] -uri = "heroku/nodejs" - -[[io.buildpacks.group]] -uri = "heroku/procfile" -``` - -Ensure that a `requirements.txt` file and a `package.json` file both exist and then build your application: - -``` -$ touch requirements.txt -$ pack build my-python-nodejs-image --path . -# ... -Successfully built image my-python-nodejs-image -``` - -You can run the image and inspect the dependencies: - -``` -$ docker run -it --rm my-python-nodejs-image bash -heroku@aef749405b22:/workspace$ python --version -Python 3.12.2 -heroku@aef749405b22:/workspace$ node --version -v20.11.1 -``` - -## Configuring your web process with the Procfile - -Most buildpacks rely on existing community standards to allow you to configure your application declaratively. They can also implement custom logic based on file contents on disk or environment variables present at build time. - -The `Procfile` is a configuration file format that was [introduced by Heroku in 2011](https://devcenter.heroku.com/articles/procfile), you can now use this behavior on your CNB-powered application via the `heroku/procfile`, which like the rest of the buildpacks in our builder [is open source](https://github.com/heroku/buildpacks-procfile). The `heroku/procfile` buildpack allows you to configure your web startup process. - -This is the Procfile of the Node.js getting started guide: - -``` -web: npm start -``` - -By including this file and using `heroku/procfile` buildpack, your application will receive a default web process that calls `npm start`. You can configure this behavior by changing the contents of that file. +Language specific tutorials moved to [docs](docs/README.md) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..88c42d8 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,108 @@ +# Using Heroku Buildpacks Locally + +This section contains tutorials for using Heroku's Cloud Native Buildpacks (CNBs) to build and run an image locally. + +## Use + +Follow along with the language-specific tutorial of your choice: + +- [.NET](dotnet/README.md) +- [Go](go/README.md) +- [Java (Gradle)](java_gradle/README.md) +- [Java (Maven)](java_maven/README.md) +- [Node.JS](nodejs/README.md) +- [PHP](php/README.md) +- [Python](python/README.md) +- [Ruby](ruby/README.md) +- [Scala](scala/README.md) + +## Development + +Tutorials are built by executing dynamic scripts with the `rundoc` tool. Source code lives in `docs/src` for each different language. + +From the root directory install RunDOC: + +``` +$ BUNDLE_GEMFILE="./.rundoc-workspace/Gemfile" bundle install +``` + +### Build Ruby + +``` +$ BUNDLE_GEMFILE="./.rundoc-workspace/Gemfile" bundle exec \ + rundoc docs/src/ruby/RUNDOC.md \ + --on-success-dir docs/ruby \ + --on-failure-dir docs/fail/ruby +``` + +### Build .NET + +``` +$ BUNDLE_GEMFILE="./.rundoc-workspace/Gemfile" bundle exec \ + rundoc docs/src/dotnet/RUNDOC.md \ + --on-success-dir docs/dotnet \ + --on-failure-dir docs/fail/dotnet +``` + +### Build Node.js + +``` +$ BUNDLE_GEMFILE="./.rundoc-workspace/Gemfile" bundle exec \ + rundoc docs/src/nodejs/RUNDOC.md \ + --on-success-dir docs/nodejs \ + --on-failure-dir docs/fail/nodejs +``` + +### Build Python + +``` +$ BUNDLE_GEMFILE="./.rundoc-workspace/Gemfile" bundle exec \ + rundoc docs/src/python/RUNDOC.md \ + --on-success-dir docs/python \ + --on-failure-dir docs/fail/python/ +``` + +### Build PHP + +``` +$ BUNDLE_GEMFILE="./.rundoc-workspace/Gemfile" bundle exec \ + rundoc docs/src/php/RUNDOC.md \ + --on-success-dir docs/php \ + --on-failure-dir docs/fail/php/ +``` + +### Build Go + +``` +$ BUNDLE_GEMFILE="./.rundoc-workspace/Gemfile" bundle exec \ + rundoc docs/src/go/RUNDOC.md \ + --on-success-dir docs/go \ + --on-failure-dir docs/fail/go/ +``` + +### Build Java (Maven) + +``` +$ BUNDLE_GEMFILE="./.rundoc-workspace/Gemfile" bundle exec \ + rundoc docs/src/java_maven/RUNDOC.md \ + --on-success-dir docs/java_maven \ + --on-failure-dir docs/fail/java_maven/ +``` + +### Build Java (Gradle) + +``` +$ BUNDLE_GEMFILE="./.rundoc-workspace/Gemfile" bundle exec \ + rundoc docs/src/java_gradle/RUNDOC.md \ + --on-success-dir docs/java_gradle \ + --on-failure-dir docs/fail/java_gradle/ +``` + +### Build Scala + +``` +$ BUNDLE_GEMFILE="./.rundoc-workspace/Gemfile" bundle exec \ + rundoc docs/src/scala/RUNDOC.md \ + --on-success-dir docs/scala \ + --on-failure-dir docs/fail/scala/ +``` diff --git a/docs/dotnet/.gitignore b/docs/dotnet/.gitignore new file mode 100644 index 0000000..34ee9d2 --- /dev/null +++ b/docs/dotnet/.gitignore @@ -0,0 +1 @@ +dotnet-getting-started diff --git a/docs/dotnet/README.md b/docs/dotnet/README.md new file mode 100644 index 0000000..7c9bab3 --- /dev/null +++ b/docs/dotnet/README.md @@ -0,0 +1,491 @@ + + +# Heroku .NET Cloud Native Buildpack (CNB) Tutorial + +Build a .NET application image in 5 minutes, no Dockerfile required. + +At the end of this tutorial, you'll have a working [OCI image](https://opencontainers.org/) of a .NET application that can run locally. You will learn about the Cloud Native Buildpack (CNB) ecosystem, and how to utilize the [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/) to build images without the need to write or maintain a Dockerfile. + +> You can now also use the `heroku/dotnet` CNB on Heroku [Fir generation](https://devcenter.heroku.com/articles/generations#fir) via the [pilot program](https://hello.heroku.com/next-generation-sign-up). See the [Getting started on Heroku Fir Dev Center tutorial](https://devcenter.heroku.com/articles/getting-started-with-dotnet-fir). + + +## Install the pack CLI + +We assume you have [docker installed](https://docs.docker.com/engine/install/) and a working copy [of git](https://github.com/git-guides/install-git). Next, you will need to install the CLI tool for building CNBs, [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/). If you're on a Mac you can install it via Homebrew: + +``` +$ brew install buildpacks/tap/pack +``` + + +Ensure that `pack` is installed correctly: + +``` +$ pack --version +0.35.1+git-3a22a7f.build-6099 +``` + +## Configure the default pack builder + +Once `pack` is installed, the only configuration you'll need for this tutorial is to set a default builder: + +``` +$ pack config default-builder heroku/builder:24 +Builder 'heroku/builder:24' is now the default builder +``` + + +You can view your default builder at any time: + +``` +$ pack config default-builder +The current default builder is 'heroku/builder:24' +``` + + +> Note: The `heroku/builder:24` supports both amd64 (also known as x86) and arm64 (such as aarch64 used with newer Mac machines) architectures. If needed, you can configure the architecture for `docker` and `pack` CLIs using the `--platform` argument if needed. For example `--platform linux/amd64`. +## What is a builder? + +> [!NOTE] +> Skip ahead if you want to build the application first and get into the details later. You won't need to +> know about builders for the rest of this tutorial. + +In short, a builder is a delivery mechanism for buildpacks. A builder contains references to base images and individual buildpacks. A base image contains the operating system and system dependencies. Buildpacks are the components that will configure an image to run your application, that’s where the bulk of the logic lives and why the project is called “Cloud Native Buildpacks” and not “Cloud Native Builders.” + +You can view the contents of a builder via the command `pack builder inspect`. For example: + +``` +$ pack builder inspect heroku/builder:24 | grep Buildpacks: -m1 -A10 +Buildpacks: + ID NAME VERSION HOMEPAGE + heroku/deb-packages Heroku .deb Packages 0.0.3 https://github.com/heroku/buildpacks-deb-packages + heroku/dotnet Heroku .NET 0.1.9 https://github.com/heroku/buildpacks-dotnet + heroku/go Heroku Go 0.4.7 https://github.com/heroku/buildpacks-go + heroku/gradle Heroku Gradle 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/java Heroku Java 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/jvm Heroku OpenJDK 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/maven Heroku Maven 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/nodejs Heroku Node.js 3.4.0 https://github.com/heroku/buildpacks-nodejs + heroku/nodejs-corepack Heroku Node.js Corepack 3.4.0 https://github.com/heroku/buildpacks-nodejs +``` + + +> [!NOTE] +> Your output version numbers may differ. + +This output shows the various buildpacks that represent the different languages that are supported by this builder such as `heroku/go` and `heroku/nodejs-engine`. +## Download an example .NET application + +How do you configure a CNB? Give them an application. While Dockerfile is procedural, buildpacks, are declarative. A buildpack will determine what your application needs to function by inspecting the code on disk. + +For this example, we're using a pre-built .NET application. Download it now: + +``` +$ git clone https://github.com/heroku/dotnet-getting-started +$ cd dotnet-getting-started +``` + + +Verify you're in the correct directory: + +``` +$ ls +Frontend +GettingStarted.sln +LICENSE.txt +Procfile +README.md +docker-compose.yml +``` + + +This tutorial was built using the following commit SHA: + +``` +$ git log --oneline | head -n1 +1957aaf Apply getting started fir guide updates (#37) +``` + +## Build the application image with the pack CLI + +Now build an image named `my-image-name` by executing the heroku builder against the application by running the +`pack build` command: + +``` +$ pack build my-image-name --path . +===> ANALYZING +Image with name "my-image-name" not found +===> DETECTING +2 of 3 buildpacks participating +heroku/dotnet 0.1.9 +heroku/procfile 3.1.2 +===> RESTORING +Skipping buildpack layer analysis +===> BUILDING + +## Heroku .NET Buildpack + +- SDK version detection + - Detected .NET file to publish: `/workspace/GettingStarted.sln` + - Inferring version requirement from `/workspace/GettingStarted.sln` + - Detected version requirement: `^8.0` + - Resolved .NET SDK version `8.0.404` (linux-arm64) +- SDK installation + - Downloading SDK from https://download.visualstudio.microsoft.com/download/pr/5ac82fcb-c260-4c46-b62f-8cde2ddfc625/feb12fc704a476ea2227c57c81d18cdf/dotnet-sdk-8.0.404-linux-arm64.tar.gz ................ (13.5s) + - Verifying SDK checksum + - Installing SDK +- Publish solution + - Using `Release` build configuration + - Running `dotnet publish /workspace/GettingStarted.sln --runtime linux-arm64 "-p:PublishDir=bin/publish"` + + Determining projects to restore... + Restored /workspace/Frontend/Frontend.csproj (in 30.32 sec). + Frontend -> /workspace/Frontend/bin/Release/net8.0/linux-arm64/Frontend.dll + Frontend -> /workspace/Frontend/bin/publish/ + Restoring .NET tools + Tool 'dotnet-ef' (version '8.0.10') was restored. Available commands: dotnet-ef + + Restore was successful. + Publishing executable database migration bundle + Build started... + Build succeeded. + Building bundle... + Done. Migrations Bundle: /workspace/Frontend/bin/publish/efbundle + Don't forget to copy appsettings.json alongside your bundle if you need it to apply migrations. + + - Done (40.0s) +- Setting launch table + - Detecting process types from published artifacts + - Added `Frontend`: bash -c cd Frontend/bin/publish; ./Frontend --urls http://*:$PORT +- Done (finished in 55.9s) + +[Discovering process types] +Procfile declares types -> web +===> EXPORTING +Adding layer 'heroku/dotnet:runtime' +Adding layer 'buildpacksio/lifecycle:launch.sbom' +Added 1/1 app layer(s) +Adding layer 'buildpacksio/lifecycle:launcher' +Adding layer 'buildpacksio/lifecycle:config' +Adding layer 'buildpacksio/lifecycle:process-types' +Adding label 'io.buildpacks.lifecycle.metadata' +Adding label 'io.buildpacks.build.metadata' +Adding label 'io.buildpacks.project.metadata' +Setting default process type 'web' +Saving my-image-name... +*** Images (34cc0dc83a19): + my-image-name +Adding cache layer 'heroku/dotnet:nuget-cache' +Adding cache layer 'heroku/dotnet:sdk' +Successfully built image 'my-image-name' +``` + + +> [!NOTE] +> Your output may differ. + +Verify that you see “Successfully built image my-image-name” at the end of the output. And verify that the image is present locally: + +``` +$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" | grep my-image-name +34cc0dc83a19 my-image-name latest +``` + +## What does `pack build` do? + +> [!NOTE] +> Skip ahead if you want to run the application first and get into the details later. + +When you run `pack build` with a builder, each buildpack runs a detection script to determine if it should be eligible to build the application. In our case the `heroku/dotnet` buildpack found a `Frontend.csproj` file. As a result, the buildpack has enough information to install .NET dependencies. You can view a list of the buildpacks used in the build output: + + +``` +===> DETECTING +2 of 3 buildpacks participating +heroku/dotnet 0.1.9 +heroku/procfile 3.1.2 +===> RESTORING +Skipping buildpack layer analysis +``` + + +After the detect phase, each buildpack will execute. Buildpacks can inspect your project, install files to disk, run commands, write environment variables, [and more](https://buildpacks.io/docs/for-buildpack-authors/). You can see some examples of that in the output above. For example, the .NET buildpack installs dependencies from the `Frontend.csproj` automatically: + +``` + - Running `dotnet publish /workspace/GettingStarted.sln --runtime linux-arm64 "-p:PublishDir=bin/publish"` +``` + + +If you’re familiar with Dockerfile you might know that [many commands in a Dockerfile will create a layer](https://dockerlabs.collabnix.com/beginners/dockerfile/Layering-Dockerfile.html). Buildpacks also use layers, but the CNB buildpack API provides for fine grained control over what exactly is in these layers and how they’re composed. Unlike Dockerfile, all images produced by CNBs [can be rebased](https://tag-env-sustainability.cncf.io/blog/2023-12-reduce-reuse-rebase-buildpacks/#reduce-reuserebase). The CNB api also improves on many of the pitfalls outlined in the satirical article [Write a Good Dockerfile in 19 'Easy' Steps](https://jkutner.github.io/2021/04/26/write-good-dockerfile.html). +## Use the image + +Even though we used `pack` and CNBs to build our image, it can be run with your favorite tools like any other OCI image. We will be using the `docker` command line to run our image. + +By default, images will be booted into a web server configuration. You can launch the app we just built by running: + +``` +$ docker run -it --rm --env PORT=5006 -p 5006:5006 my-image-name +warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60] + Storing keys in a directory '/home/heroku/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed. For more information go to https://aka.ms/aspnet/dataprotectionwarning +warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35] + No XML encryptor configured. Key {2b312d6e-ac53-4ea5-b31f-f327da08ec51} may be persisted to storage in unencrypted form. +info: Microsoft.Hosting.Lifetime[14] + Now listening on: http://[::]:5006 +info: Microsoft.Hosting.Lifetime[0] + Application started. Press Ctrl+C to shut down. +info: Microsoft.Hosting.Lifetime[0] + Hosting environment: Production +info: Microsoft.Hosting.Lifetime[0] + Content root path: /workspace/Frontend/bin/publish +``` + + +Now when you visit [http://localhost:5006](http://localhost:5006) you should see a working web application: + +![Screenshot of http://localhost:5006/](screenshots/screenshot_1.png) + +Don't forget to stop the docker container when you're done. + + +Here's a quick breakdown of that command we just ran: + +- `docker run` Create and run a new container from an image. +- `-it` Makes the container interactive and allocates a TTY. +- `--rm` Automatically remove the container when it exits. +- `--env PORT=5006` Creates an environment variable named `PORT` and sets it to `5006` this is needed so the application inside the container knows what port to bind the web server. +- `-p 5006:5006` Publishes a container's port(s) to the host. This is what allows requests from your machine to be received by the container. +- `my-image-name` The name of the image you want to use for the application. + +So far, we've downloaded an application via git and run a single command `pack build` to generate an image, and then we can use that image as if it was generated via a Dockerfile via the `docker run` command. + +In addition to running the image as a web server, you can access the container's terminal interactively. In a new terminal window try running this command: + +``` +$ docker run -it --rm my-image-name bash +``` + + +Now you can inspect the container interactively. For example, you can see the files on disk with `ls`: + +``` +$ ls +Frontend +GettingStarted.sln +LICENSE.txt +Procfile +README.md +build_output.txt +docker-compose.yml +``` + + +And anything else you would typically do via an interactive container session. +## Image structure under the hood + +> [!NOTE] +> Skip this section if you want to try building your application with CNBs and learn about container structure later. + +If you’re an advanced `Dockerfile` user you might be interested in learning more about the internal structure of the image on disk. You can access the image disk interactively by using the `bash` docker command above. + +If you view the root directory `/` you’ll see there is a `layers` folder. Every buildpack that executes gets a unique folder: + +``` +$ docker run --rm my-image-name "ls /layers" +config +heroku_dotnet +sbom +``` + + +Individual buildpacks can compose multiple layers from their buildpack directory. For example you can see that `dotnet` binary is present within that buildpack layer directory: + +``` +$ docker run --rm my-image-name "which dotnet" +/layers/heroku_dotnet/runtime/dotnet +``` + + +OCI images are represented as sequential modifications to disk. By scoping buildpack disk modifications to their own directory, the CNB API guarantees that changes to a layer in one buildpack will not affect the contents of disk to another layer. This means that OCI images produced by CNBs are rebaseable by default, while those produced by Dockerfile are not. + +We saw before how the image booted a web server by default. This is accomplished using an entrypoint. In another terminal outside of the running container you can view that entrypoint: + +``` +$ docker inspect my-image-name | grep '"Entrypoint": \[' -A2 + "Entrypoint": [ + "/cnb/process/web" + ], +``` + + +From within the image, you can see that file on disk: + +``` +$ docker run --rm my-image-name "ls /cnb/process/" +Frontend +web +``` + + +While you might not need this level of detail to build and run an application with Cloud Native Buildpacks, it is useful to understand how they’re structured if you ever want to write your own buildpack. +## Try CNBs out on your application + +So far we've learned that CNBs are a declarative interface for producing OCI images (like docker). They aim to be no to low configuration and once built, you can interact with them like any other image. + +For the next step, we encourage you to try running `pack` with the Heroku builder against your application and let us know how it went. We encourage you to share your experience by [opening a discussion](https://github.com/heroku/buildpacks/discussions) and walking us through what happened: + +- What went well? +- What could be better? +- Do you have any questions? + +We are actively working on our Cloud Native Buildpacks and want to hear about your experience. The documentation below covers some intermediate-level topics that you might find helpful. +Language support is provided by individual buildpacks that are shipped with the builder. The above example uses the `heroku/dotnet` buildpack which is [visible on GitHub](https://github.com/heroku/buildpacks-dotnet). When you execute `pack build` with a builder, every buildpack has the opportunity to "detect" if it should execute against that project. The `heroku/dotnet` buildpack looks for a file in the root of the project and if found, knows how to detect a dotnet version and install dependencies. + +In addition to this auto-detection behavior, you can specify buildpacks through the `--buildpack` flag with the `pack` CLI or through a [project.toml](https://buildpacks.io/docs/for-app-developers/how-to/build-inputs/specify-buildpacks/) file at the root of your application. + +For example, if you wanted to install both dotnet and Python you could create a `project.toml` file in the root of your application and specify those buildpacks. +```md +## Configuring multiple languages +``` + + +In file `project.toml` write: + +```toml +[_] +schema-version = "0.2" +id = "sample.dotnet+python.app" +name = "Sample .NET & Python App" +version = "1.0.0" + +[[io.buildpacks.group]] +uri = "heroku/python" + +[[io.buildpacks.group]] +uri = "heroku/dotnet" + +[[io.buildpacks.group]] +uri = "heroku/procfile" +``` + + +Ensure that a `requirements.txt` file, a web solution file all exist and then build your application: + +``` +$ pack build my-image-name --path . +===> ANALYZING +Image with name "my-image-name" not found +===> DETECTING +heroku/python 0.20.0 +heroku/dotnet 0.1.9 +heroku/procfile 3.1.2 +===> RESTORING +Skipping buildpack layer analysis +===> BUILDING + +[Determining Python version] +No Python version specified, using the current default of Python 3.13. +We recommend setting an explicit version. In the root of your app create +a '.python-version' file, containing a Python version like '3.13'. + +[Installing Python] +Installing Python 3.13.1 + +[Installing pip] +Installing pip 24.3.1 + +[Installing dependencies using pip] +Creating virtual environment +Running 'pip install -r requirements.txt' + +## Heroku .NET Buildpack + +- SDK version detection + - Detected .NET file to publish: `/workspace/GettingStarted.sln` + - Inferring version requirement from `/workspace/GettingStarted.sln` + - Detected version requirement: `^8.0` + - Resolved .NET SDK version `8.0.404` (linux-arm64) +- SDK installation + - Downloading SDK from https://download.visualstudio.microsoft.com/download/pr/5ac82fcb-c260-4c46-b62f-8cde2ddfc625/feb12fc704a476ea2227c57c81d18cdf/dotnet-sdk-8.0.404-linux-arm64.tar.gz .............. (11.4s) + - Verifying SDK checksum + - Installing SDK +- Publish solution + - Using `Release` build configuration + - Running `dotnet publish /workspace/GettingStarted.sln --runtime linux-arm64 "-p:PublishDir=bin/publish"` + + Determining projects to restore... + Restored /workspace/Frontend/Frontend.csproj (in 26.93 sec). + Frontend -> /workspace/Frontend/bin/Release/net8.0/linux-arm64/Frontend.dll + Frontend -> /workspace/Frontend/bin/publish/ + Restoring .NET tools + Tool 'dotnet-ef' (version '8.0.10') was restored. Available commands: dotnet-ef + + Restore was successful. + Publishing executable database migration bundle + Build started... + Build succeeded. + Building bundle... + Done. Migrations Bundle: /workspace/Frontend/bin/publish/efbundle + Don't forget to copy appsettings.json alongside your bundle if you need it to apply migrations. + + - Done (37.3s) +- Setting launch table + - Detecting process types from published artifacts + - Added `Frontend`: bash -c cd Frontend/bin/publish; ./Frontend --urls http://*:$PORT +- Done (finished in 51.4s) + +[Discovering process types] +Procfile declares types -> web +===> EXPORTING +Adding layer 'heroku/python:python' +Adding layer 'heroku/python:venv' +Adding layer 'heroku/dotnet:runtime' +Adding layer 'buildpacksio/lifecycle:launch.sbom' +Added 1/1 app layer(s) +Adding layer 'buildpacksio/lifecycle:launcher' +Adding layer 'buildpacksio/lifecycle:config' +Adding layer 'buildpacksio/lifecycle:process-types' +Adding label 'io.buildpacks.lifecycle.metadata' +Adding label 'io.buildpacks.build.metadata' +Adding label 'io.buildpacks.project.metadata' +Setting default process type 'web' +Saving my-image-name... +*** Images (73c0b944e8fc): + my-image-name +Adding cache layer 'heroku/python:pip' +Adding cache layer 'heroku/python:pip-cache' +Adding cache layer 'heroku/python:python' +Adding cache layer 'heroku/dotnet:nuget-cache' +Adding cache layer 'heroku/dotnet:sdk' +Successfully built image 'my-image-name' +``` + + +You can run the image and inspect everything is installed as expected: + +``` +$ docker run -it --rm my-image-name bash +$ which python +/layers/heroku_python/venv/bin/python +``` + +## Configuring your web process with the Procfile + +Most buildpacks rely on existing community standards to allow you to configure your application declaratively. They can also implement custom logic based on file contents on disk or environment variables present at build time. + +The `Procfile` is a configuration file format that was [introduced by Heroku in 2011](https://devcenter.heroku.com/articles/procfile), you can now use this behavior on your CNB-powered application via the `heroku/procfile`, which like the rest of the buildpacks in our builder [is open source](https://github.com/heroku/buildpacks-procfile). The `heroku/procfile` buildpack allows you to configure your web startup process. + +This is the `Procfile` of the getting started guide: + +``` +web: cd Frontend/bin/publish/; ./Frontend --urls http://*:$PORT +``` + + +By including this file and using `heroku/procfile` buildpack, your application will receive a default web process. You can configure this behavior by changing the contents of that file. + diff --git a/docs/dotnet/screenshots/screenshot_1.png b/docs/dotnet/screenshots/screenshot_1.png new file mode 100644 index 0000000..6d75d3c Binary files /dev/null and b/docs/dotnet/screenshots/screenshot_1.png differ diff --git a/docs/go/.gitignore b/docs/go/.gitignore new file mode 100644 index 0000000..c3f9bd8 --- /dev/null +++ b/docs/go/.gitignore @@ -0,0 +1 @@ +go-getting-started diff --git a/docs/go/README.md b/docs/go/README.md new file mode 100644 index 0000000..1bde4ac --- /dev/null +++ b/docs/go/README.md @@ -0,0 +1,355 @@ + + +# Heroku Go Cloud Native Buildpack (CNB) Tutorial + +Build a Go application image in 5 minutes, no Dockerfile required. + +At the end of this tutorial, you'll have a working [OCI image](https://opencontainers.org/) of a Go application that can run locally. You will learn about the Cloud Native Buildpack (CNB) ecosystem, and how to utilize the [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/) to build images without the need to write or maintain a Dockerfile. + +> You can now also use the `heroku/go` CNB on Heroku [Fir generation](https://devcenter.heroku.com/articles/generations#fir) via the [pilot program](https://hello.heroku.com/next-generation-sign-up). See the [Getting started on Heroku Fir Dev Center tutorial](https://devcenter.heroku.com/articles/getting-started-with-go-fir). + + +## Install the pack CLI + +We assume you have [docker installed](https://docs.docker.com/engine/install/) and a working copy [of git](https://github.com/git-guides/install-git). Next, you will need to install the CLI tool for building CNBs, [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/). If you're on a Mac you can install it via Homebrew: + +``` +$ brew install buildpacks/tap/pack +``` + + +Ensure that `pack` is installed correctly: + +``` +$ pack --version +0.35.1+git-3a22a7f.build-6099 +``` + +## Configure the default pack builder + +Once `pack` is installed, the only configuration you'll need for this tutorial is to set a default builder: + +``` +$ pack config default-builder heroku/builder:24 +Builder 'heroku/builder:24' is now the default builder +``` + + +You can view your default builder at any time: + +``` +$ pack config default-builder +The current default builder is 'heroku/builder:24' +``` + + +> Note: The `heroku/builder:24` supports both amd64 (also known as x86) and arm64 (such as aarch64 used with newer Mac machines) architectures. If needed, you can configure the architecture for `docker` and `pack` CLIs using the `--platform` argument if needed. For example `--platform linux/amd64`. +## What is a builder? + +> [!NOTE] +> Skip ahead if you want to build the application first and get into the details later. You won't need to +> know about builders for the rest of this tutorial. + +In short, a builder is a delivery mechanism for buildpacks. A builder contains references to base images and individual buildpacks. A base image contains the operating system and system dependencies. Buildpacks are the components that will configure an image to run your application, that’s where the bulk of the logic lives and why the project is called “Cloud Native Buildpacks” and not “Cloud Native Builders.” + +You can view the contents of a builder via the command `pack builder inspect`. For example: + +``` +$ pack builder inspect heroku/builder:24 | grep Buildpacks: -m1 -A10 +Buildpacks: + ID NAME VERSION HOMEPAGE + heroku/deb-packages Heroku .deb Packages 0.0.3 https://github.com/heroku/buildpacks-deb-packages + heroku/dotnet Heroku .NET 0.1.9 https://github.com/heroku/buildpacks-dotnet + heroku/go Heroku Go 0.5.0 https://github.com/heroku/buildpacks-go + heroku/gradle Heroku Gradle 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/java Heroku Java 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/jvm Heroku OpenJDK 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/maven Heroku Maven 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/nodejs Heroku Node.js 3.4.0 https://github.com/heroku/buildpacks-nodejs + heroku/nodejs-corepack Heroku Node.js Corepack 3.4.0 https://github.com/heroku/buildpacks-nodejs +``` + + +> [!NOTE] +> Your output version numbers may differ. + +This output shows the various buildpacks that represent the different languages that are supported by this builder such as `heroku/go` and `heroku/nodejs-engine`. +## Download an example Go application + +How do you configure a CNB? Give them an application. While Dockerfile is procedural, buildpacks, are declarative. A buildpack will determine what your application needs to function by inspecting the code on disk. + +For this example, we're using a pre-built Go application. Download it now: + +``` +$ git clone https://github.com/heroku/go-getting-started +$ cd go-getting-started +``` + + +Verify you're in the correct directory: + +``` +$ ls +Dockerfile +Makefile +Procfile +README.md +app.json +go.mod +go.sum +heroku.yml +main.go +static +templates +vendor +``` + + +This tutorial was built using the following commit SHA: + +``` +$ git log --oneline | head -n1 +3b63dd0 Update readme and index to add Fir (#66) +``` + +## Build the application image with the pack CLI + +Now build an image named `my-image-name` by executing the heroku builder against the application by running the +`pack build` command: + +``` +$ pack build my-image-name --path . +===> ANALYZING +Image with name "my-image-name" not found +===> DETECTING +2 of 3 buildpacks participating +heroku/go 0.5.0 +heroku/procfile 3.1.2 +===> RESTORING +Skipping buildpack layer analysis +===> BUILDING + +[Reading build configuration] +Detected Go version requirement: =1.20 +Resolved Go version: go1.20.14 (linux-arm64) + +[Installing Go distribution] +Installing go1.20.14 (linux-arm64) from https://go.dev/dl/go1.20.14.linux-arm64.tar.gz + +[Building Go binaries] +Using vendored Go modules +Creating Go build cache +Resolving Go modules +Building packages: + - github.com/heroku/go-getting-started +Skipping launch process registration (Procfile detected) + +[Discovering process types] +Procfile declares types -> web +===> EXPORTING +Adding layer 'heroku/go:go_target' +Adding layer 'buildpacksio/lifecycle:launch.sbom' +Added 1/1 app layer(s) +Adding layer 'buildpacksio/lifecycle:launcher' +Adding layer 'buildpacksio/lifecycle:config' +Adding layer 'buildpacksio/lifecycle:process-types' +Adding label 'io.buildpacks.lifecycle.metadata' +Adding label 'io.buildpacks.build.metadata' +Adding label 'io.buildpacks.project.metadata' +Setting default process type 'web' +Saving my-image-name... +*** Images (6254e65c3c92): + my-image-name +Adding cache layer 'heroku/go:go_build' +Adding cache layer 'heroku/go:go_dist' +Successfully built image 'my-image-name' +``` + + +> [!NOTE] +> Your output may differ. + +Verify that you see “Successfully built image my-image-name” at the end of the output. And verify that the image is present locally: + +``` +$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" | grep my-image-name +6254e65c3c92 my-image-name latest +``` + +## What does `pack build` do? + +> [!NOTE] +> Skip ahead if you want to run the application first and get into the details later. + +When you run `pack build` with a builder, each buildpack runs a detection script to determine if it should be eligible to build the application. In our case the `heroku/go` buildpack found a `go.mod` file. As a result, the buildpack has enough information to install Go dependencies. You can view a list of the buildpacks used in the build output: + + +``` +===> DETECTING +2 of 3 buildpacks participating +heroku/go 0.5.0 +heroku/procfile 3.1.2 +===> RESTORING +Skipping buildpack layer analysis +``` + + +After the detect phase, each buildpack will execute. Buildpacks can inspect your project, install files to disk, run commands, write environment variables, [and more](https://buildpacks.io/docs/for-buildpack-authors/). You can see some examples of that in the output above. For example, the Go buildpack installs dependencies from the `go.mod` automatically: + +``` +[Building Go binaries] +``` + + +If you’re familiar with Dockerfile you might know that [many commands in a Dockerfile will create a layer](https://dockerlabs.collabnix.com/beginners/dockerfile/Layering-Dockerfile.html). Buildpacks also use layers, but the CNB buildpack API provides for fine grained control over what exactly is in these layers and how they’re composed. Unlike Dockerfile, all images produced by CNBs [can be rebased](https://tag-env-sustainability.cncf.io/blog/2023-12-reduce-reuse-rebase-buildpacks/#reduce-reuserebase). The CNB api also improves on many of the pitfalls outlined in the satirical article [Write a Good Dockerfile in 19 'Easy' Steps](https://jkutner.github.io/2021/04/26/write-good-dockerfile.html). +## Use the image + +Even though we used `pack` and CNBs to build our image, it can be run with your favorite tools like any other OCI image. We will be using the `docker` command line to run our image. + +By default, images will be booted into a web server configuration. You can launch the app we just built by running: + +``` +$ docker run -it --rm --env PORT=5006 -p 5006:5006 my-image-name +[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. + - using env: export GIN_MODE=release + - using code: gin.SetMode(gin.ReleaseMode) + +[GIN-debug] Loaded HTML Templates (4): + - header.tmpl.html + - index.tmpl.html + - nav.tmpl.html + - + +[GIN-debug] GET /static/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (2 handlers) +[GIN-debug] HEAD /static/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (2 handlers) +[GIN-debug] GET / --> main.main.func1 (2 handlers) +[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value. +Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details. +[GIN-debug] Listening and serving HTTP on :5006 +``` + + +Now when you visit [http://localhost:5006](http://localhost:5006) you should see a working web application: + +![Screenshot of http://localhost:5006/](screenshots/screenshot_1.png) + +Don't forget to stop the docker container when you're done. + + +Here's a quick breakdown of that command we just ran: + +- `docker run` Create and run a new container from an image. +- `-it` Makes the container interactive and allocates a TTY. +- `--rm` Automatically remove the container when it exits. +- `--env PORT=5006` Creates an environment variable named `PORT` and sets it to `5006` this is needed so the application inside the container knows what port to bind the web server. +- `-p 5006:5006` Publishes a container's port(s) to the host. This is what allows requests from your machine to be received by the container. +- `my-image-name` The name of the image you want to use for the application. + +So far, we've downloaded an application via git and run a single command `pack build` to generate an image, and then we can use that image as if it was generated via a Dockerfile via the `docker run` command. + +In addition to running the image as a web server, you can access the container's terminal interactively. In a new terminal window try running this command: + +``` +$ docker run -it --rm my-image-name bash +``` + + +Now you can inspect the container interactively. For example, you can see the files on disk with `ls`: + +``` +$ ls +Dockerfile +Makefile +Procfile +README.md +app.json +build_output.txt +go.mod +go.sum +heroku.yml +main.go +static +templates +vendor +``` + + +And anything else you would typically do via an interactive container session. +## Image structure under the hood + +> [!NOTE] +> Skip this section if you want to try building your application with CNBs and learn about container structure later. + +If you’re an advanced `Dockerfile` user you might be interested in learning more about the internal structure of the image on disk. You can access the image disk interactively by using the `bash` docker command above. + +If you view the root directory `/` you’ll see there is a `layers` folder. Every buildpack that executes gets a unique folder: + +``` +$ docker run --rm my-image-name "ls /layers" +config +heroku_go +sbom +``` + + +Individual buildpacks can compose multiple layers from their buildpack directory. For example you can see that `go-getting-started` binary is present within that buildpack layer directory: + +``` +$ docker run --rm my-image-name "which go-getting-started" +/layers/heroku_go/go_target/bin/go-getting-started +``` + + +OCI images are represented as sequential modifications to disk. By scoping buildpack disk modifications to their own directory, the CNB API guarantees that changes to a layer in one buildpack will not affect the contents of disk to another layer. This means that OCI images produced by CNBs are rebaseable by default, while those produced by Dockerfile are not. + +We saw before how the image booted a web server by default. This is accomplished using an entrypoint. In another terminal outside of the running container you can view that entrypoint: + +``` +$ docker inspect my-image-name | grep '"Entrypoint": \[' -A2 + "Entrypoint": [ + "/cnb/process/web" + ], +``` + + +From within the image, you can see that file on disk: + +``` +$ docker run --rm my-image-name "ls /cnb/process/" +web +``` + + +While you might not need this level of detail to build and run an application with Cloud Native Buildpacks, it is useful to understand how they’re structured if you ever want to write your own buildpack. +## Try CNBs out on your application + +So far we've learned that CNBs are a declarative interface for producing OCI images (like docker). They aim to be no to low configuration and once built, you can interact with them like any other image. + +For the next step, we encourage you to try running `pack` with the Heroku builder against your application and let us know how it went. We encourage you to share your experience by [opening a discussion](https://github.com/heroku/buildpacks/discussions) and walking us through what happened: + +- What went well? +- What could be better? +- Do you have any questions? + +We are actively working on our Cloud Native Buildpacks and want to hear about your experience. The documentation below covers some intermediate-level topics that you might find helpful. +## Configuring your web process with the Procfile + +Most buildpacks rely on existing community standards to allow you to configure your application declaratively. They can also implement custom logic based on file contents on disk or environment variables present at build time. + +The `Procfile` is a configuration file format that was [introduced by Heroku in 2011](https://devcenter.heroku.com/articles/procfile), you can now use this behavior on your CNB-powered application via the `heroku/procfile`, which like the rest of the buildpacks in our builder [is open source](https://github.com/heroku/buildpacks-procfile). The `heroku/procfile` buildpack allows you to configure your web startup process. + +This is the `Procfile` of the getting started guide: + +``` +web: go-getting-started +``` + + +By including this file and using `heroku/procfile` buildpack, your application will receive a default web process. You can configure this behavior by changing the contents of that file. + diff --git a/docs/go/screenshots/screenshot_1.png b/docs/go/screenshots/screenshot_1.png new file mode 100644 index 0000000..27bac95 Binary files /dev/null and b/docs/go/screenshots/screenshot_1.png differ diff --git a/docs/java_gradle/.gitignore b/docs/java_gradle/.gitignore new file mode 100644 index 0000000..17c781f --- /dev/null +++ b/docs/java_gradle/.gitignore @@ -0,0 +1 @@ +gradle-getting-started diff --git a/docs/java_gradle/README.md b/docs/java_gradle/README.md new file mode 100644 index 0000000..a8a0e3d --- /dev/null +++ b/docs/java_gradle/README.md @@ -0,0 +1,375 @@ + + +# Heroku Java Cloud Native Buildpack (CNB) Tutorial + +Build a Java (Gradle) application image in 5 minutes, no Dockerfile required. + +At the end of this tutorial, you'll have a working [OCI image](https://opencontainers.org/) of a Java (Gradle) application that can run locally. You will learn about the Cloud Native Buildpack (CNB) ecosystem, and how to utilize the [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/) to build images without the need to write or maintain a Dockerfile. + +> You can now also use the `heroku/java` CNB on Heroku [Fir generation](https://devcenter.heroku.com/articles/generations#fir) via the [pilot program](https://hello.heroku.com/next-generation-sign-up). See the [Getting started on Heroku Fir Dev Center tutorial](https://devcenter.heroku.com/articles/getting-started-with-java-gradle-fir). + + +## Install the pack CLI + +We assume you have [docker installed](https://docs.docker.com/engine/install/) and a working copy [of git](https://github.com/git-guides/install-git). Next, you will need to install the CLI tool for building CNBs, [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/). If you're on a Mac you can install it via Homebrew: + +``` +$ brew install buildpacks/tap/pack +``` + + +Ensure that `pack` is installed correctly: + +``` +$ pack --version +0.35.1+git-3a22a7f.build-6099 +``` + +## Configure the default pack builder + +Once `pack` is installed, the only configuration you'll need for this tutorial is to set a default builder: + +``` +$ pack config default-builder heroku/builder:24 +Builder 'heroku/builder:24' is now the default builder +``` + + +You can view your default builder at any time: + +``` +$ pack config default-builder +The current default builder is 'heroku/builder:24' +``` + + +> Note: The `heroku/builder:24` supports both amd64 (also known as x86) and arm64 (such as aarch64 used with newer Mac machines) architectures. If needed, you can configure the architecture for `docker` and `pack` CLIs using the `--platform` argument if needed. For example `--platform linux/amd64`. +## What is a builder? + +> [!NOTE] +> Skip ahead if you want to build the application first and get into the details later. You won't need to +> know about builders for the rest of this tutorial. + +In short, a builder is a delivery mechanism for buildpacks. A builder contains references to base images and individual buildpacks. A base image contains the operating system and system dependencies. Buildpacks are the components that will configure an image to run your application, that’s where the bulk of the logic lives and why the project is called “Cloud Native Buildpacks” and not “Cloud Native Builders.” + +You can view the contents of a builder via the command `pack builder inspect`. For example: + +``` +$ pack builder inspect heroku/builder:24 | grep Buildpacks: -m1 -A10 +Buildpacks: + ID NAME VERSION HOMEPAGE + heroku/deb-packages Heroku .deb Packages 0.0.3 https://github.com/heroku/buildpacks-deb-packages + heroku/dotnet Heroku .NET 0.1.9 https://github.com/heroku/buildpacks-dotnet + heroku/go Heroku Go 0.5.0 https://github.com/heroku/buildpacks-go + heroku/gradle Heroku Gradle 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/java Heroku Java 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/jvm Heroku OpenJDK 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/maven Heroku Maven 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/nodejs Heroku Node.js 3.4.0 https://github.com/heroku/buildpacks-nodejs + heroku/nodejs-corepack Heroku Node.js Corepack 3.4.0 https://github.com/heroku/buildpacks-nodejs +``` + + +> [!NOTE] +> Your output version numbers may differ. + +This output shows the various buildpacks that represent the different languages that are supported by this builder such as `heroku/go` and `heroku/nodejs-engine`. +## Download an example Java (Gradle) application + +How do you configure a CNB? Give them an application. While Dockerfile is procedural, buildpacks, are declarative. A buildpack will determine what your application needs to function by inspecting the code on disk. + +For this example, we're using a pre-built Java (Gradle) application. Download it now: + +``` +$ git clone https://github.com/heroku/gradle-getting-started +$ cd gradle-getting-started +``` + + +Verify you're in the correct directory: + +``` +$ ls +LICENSE +Procfile +README.md +app.json +build.gradle.kts +gradle +gradlew +gradlew.bat +settings.gradle.kts +src +system.properties +``` + + +This tutorial was built using the following commit SHA: + +``` +$ git log --oneline | head -n1 +3b16adc Update for Fir (#78) +``` + +## Build the application image with the pack CLI + +Now build an image named `my-image-name` by executing the heroku builder against the application by running the +`pack build` command: + +``` +$ pack build my-image-name --path . +===> ANALYZING +Image with name "my-image-name" not found +===> DETECTING +3 of 4 buildpacks participating +heroku/jvm 6.0.4 +heroku/gradle 6.0.4 +heroku/procfile 3.1.2 +===> RESTORING +Skipping buildpack layer analysis +===> BUILDING + +[Installing OpenJDK 17.0.13] + +[Gradle Buildpack] + +[Starting Gradle Daemon] +Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 +Downloading https://services.gradle.org/distributions/gradle-7.6.1-bin.zip +...........10%............20%...........30%............40%............50%...........60%............70%............80%...........90%............100% +Starting a Gradle Daemon (subsequent builds will be faster) +> Task :heroku_buildpack_start_daemon UP-TO-DATE + +BUILD SUCCESSFUL in 20s + +[Running build task] +Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 +> Task :compileJava +> Task :processResources +> Task :classes +> Task :resolveMainClassName +> Task :bootJar +> Task :jar +> Task :assemble +> Task :build + +BUILD SUCCESSFUL in 3s +5 actionable tasks: 5 executed +Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 + +[Discovering process types] +Procfile declares types -> web +===> EXPORTING +Adding layer 'heroku/jvm:openjdk' +Adding layer 'heroku/jvm:runtime' +Adding layer 'heroku/gradle:home' +Adding layer 'buildpacksio/lifecycle:launch.sbom' +Added 1/1 app layer(s) +Adding layer 'buildpacksio/lifecycle:launcher' +Adding layer 'buildpacksio/lifecycle:config' +Adding layer 'buildpacksio/lifecycle:process-types' +Adding label 'io.buildpacks.lifecycle.metadata' +Adding label 'io.buildpacks.build.metadata' +Adding label 'io.buildpacks.project.metadata' +Setting default process type 'web' +Saving my-image-name... +*** Images (b93f128d32ca): + my-image-name +Adding cache layer 'heroku/jvm:openjdk' +Adding cache layer 'heroku/gradle:home' +Successfully built image 'my-image-name' +``` + + +> [!NOTE] +> Your output may differ. + +Verify that you see “Successfully built image my-image-name” at the end of the output. And verify that the image is present locally: + +``` +$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" | grep my-image-name +b93f128d32ca my-image-name latest +``` + +## What does `pack build` do? + +> [!NOTE] +> Skip ahead if you want to run the application first and get into the details later. + +When you run `pack build` with a builder, each buildpack runs a detection script to determine if it should be eligible to build the application. In our case the `heroku/java` buildpack found a `build.gradle.kts` file. As a result, the buildpack has enough information to install Java dependencies. You can view a list of the buildpacks used in the build output: + + +``` +===> DETECTING +3 of 4 buildpacks participating +heroku/jvm 6.0.4 +heroku/gradle 6.0.4 +heroku/procfile 3.1.2 +===> RESTORING +``` + + +After the detect phase, each buildpack will execute. Buildpacks can inspect your project, install files to disk, run commands, write environment variables, [and more](https://buildpacks.io/docs/for-buildpack-authors/). You can see some examples of that in the output above. For example, the Java buildpack installs dependencies from the `build.gradle.kts` automatically: + +``` +> Task :compileJava +``` + + +If you’re familiar with Dockerfile you might know that [many commands in a Dockerfile will create a layer](https://dockerlabs.collabnix.com/beginners/dockerfile/Layering-Dockerfile.html). Buildpacks also use layers, but the CNB buildpack API provides for fine grained control over what exactly is in these layers and how they’re composed. Unlike Dockerfile, all images produced by CNBs [can be rebased](https://tag-env-sustainability.cncf.io/blog/2023-12-reduce-reuse-rebase-buildpacks/#reduce-reuserebase). The CNB api also improves on many of the pitfalls outlined in the satirical article [Write a Good Dockerfile in 19 'Easy' Steps](https://jkutner.github.io/2021/04/26/write-good-dockerfile.html). +## Use the image + +Even though we used `pack` and CNBs to build our image, it can be run with your favorite tools like any other OCI image. We will be using the `docker` command line to run our image. + +By default, images will be booted into a web server configuration. You can launch the app we just built by running: + +``` +$ docker run -it --rm --env PORT=5006 -p 5006:5006 my-image-name +Picked up JAVA_TOOL_OPTIONS: -XX:MaxRAMPercentage=80.0 -Dfile.encoding=UTF-8 + _ _ _ + | | | | | | + | |__| | ___ _ __ ___ | | ___ _ + | __ |/ _ \ '__/ _ \| |/ / | | | + | | | | __/ | | (_) | <| |_| | + |_| |_|\___|_| \___/|_|\_\\__,_| + +:: Built with Spring Boot :: 3.3.5 + +2024-12-15T01:53:12.465Z INFO 1 --- [ main] c.heroku.java.GettingStartedApplication : Starting GettingStartedApplication v1.0.0-SNAPSHOT using Java 17.0.13 with PID 1 (/workspace/build/libs/java-getting-started-gradle-1.0.0-SNAPSHOT.jar started by heroku in /workspace) +2024-12-15T01:53:12.466Z INFO 1 --- [ main] c.heroku.java.GettingStartedApplication : No active profile set, falling back to 1 default profile: "default" +2024-12-15T01:53:12.935Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 5006 (http) +2024-12-15T01:53:12.943Z INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2024-12-15T01:53:12.943Z INFO 1 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.31] +2024-12-15T01:53:12.959Z INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2024-12-15T01:53:12.959Z INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 470 ms +2024-12-15T01:53:13.019Z INFO 1 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index +2024-12-15T01:53:13.163Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 5006 (http) with context path '/' +2024-12-15T01:53:13.171Z INFO 1 --- [ main] c.heroku.java.GettingStartedApplication : Started GettingStartedApplication in 0.873 seconds (process running for 1.079) +``` + + +Now when you visit [http://localhost:5006](http://localhost:5006) you should see a working web application: + +![Screenshot of http://localhost:5006/](screenshots/screenshot_1.png) + +Don't forget to stop the docker container when you're done. + + +Here's a quick breakdown of that command we just ran: + +- `docker run` Create and run a new container from an image. +- `-it` Makes the container interactive and allocates a TTY. +- `--rm` Automatically remove the container when it exits. +- `--env PORT=5006` Creates an environment variable named `PORT` and sets it to `5006` this is needed so the application inside the container knows what port to bind the web server. +- `-p 5006:5006` Publishes a container's port(s) to the host. This is what allows requests from your machine to be received by the container. +- `my-image-name` The name of the image you want to use for the application. + +So far, we've downloaded an application via git and run a single command `pack build` to generate an image, and then we can use that image as if it was generated via a Dockerfile via the `docker run` command. + +In addition to running the image as a web server, you can access the container's terminal interactively. In a new terminal window try running this command: + +``` +$ docker run -it --rm my-image-name bash +``` + + +Now you can inspect the container interactively. For example, you can see the files on disk with `ls`: + +``` +$ ls +LICENSE +Procfile +README.md +app.json +build +build.gradle.kts +build_output.txt +gradle +gradlew +gradlew.bat +settings.gradle.kts +src +system.properties +``` + + +And anything else you would typically do via an interactive container session. +## Image structure under the hood + +> [!NOTE] +> Skip this section if you want to try building your application with CNBs and learn about container structure later. + +If you’re an advanced `Dockerfile` user you might be interested in learning more about the internal structure of the image on disk. You can access the image disk interactively by using the `bash` docker command above. + +If you view the root directory `/` you’ll see there is a `layers` folder. Every buildpack that executes gets a unique folder: + +``` +$ docker run --rm my-image-name "ls /layers" +config +heroku_gradle +heroku_jvm +sbom +``` + + +Individual buildpacks can compose multiple layers from their buildpack directory. For example you can see that `java` binary is present within that buildpack layer directory: + +``` +$ docker run --rm my-image-name "which java" +/layers/heroku_jvm/openjdk/bin/java +``` + + +OCI images are represented as sequential modifications to disk. By scoping buildpack disk modifications to their own directory, the CNB API guarantees that changes to a layer in one buildpack will not affect the contents of disk to another layer. This means that OCI images produced by CNBs are rebaseable by default, while those produced by Dockerfile are not. + +We saw before how the image booted a web server by default. This is accomplished using an entrypoint. In another terminal outside of the running container you can view that entrypoint: + +``` +$ docker inspect my-image-name | grep '"Entrypoint": \[' -A2 + "Entrypoint": [ + "/cnb/process/web" + ], +``` + + +From within the image, you can see that file on disk: + +``` +$ docker run --rm my-image-name "ls /cnb/process/" +web +``` + + +While you might not need this level of detail to build and run an application with Cloud Native Buildpacks, it is useful to understand how they’re structured if you ever want to write your own buildpack. +## Try CNBs out on your application + +So far we've learned that CNBs are a declarative interface for producing OCI images (like docker). They aim to be no to low configuration and once built, you can interact with them like any other image. + +For the next step, we encourage you to try running `pack` with the Heroku builder against your application and let us know how it went. We encourage you to share your experience by [opening a discussion](https://github.com/heroku/buildpacks/discussions) and walking us through what happened: + +- What went well? +- What could be better? +- Do you have any questions? + +We are actively working on our Cloud Native Buildpacks and want to hear about your experience. The documentation below covers some intermediate-level topics that you might find helpful. +## Configuring your web process with the Procfile + +Most buildpacks rely on existing community standards to allow you to configure your application declaratively. They can also implement custom logic based on file contents on disk or environment variables present at build time. + +The `Procfile` is a configuration file format that was [introduced by Heroku in 2011](https://devcenter.heroku.com/articles/procfile), you can now use this behavior on your CNB-powered application via the `heroku/procfile`, which like the rest of the buildpacks in our builder [is open source](https://github.com/heroku/buildpacks-procfile). The `heroku/procfile` buildpack allows you to configure your web startup process. + +This is the `Procfile` of the getting started guide: + +``` +web: java -jar build/libs/java-getting-started-gradle-1.0.0-SNAPSHOT.jar +``` + + +By including this file and using `heroku/procfile` buildpack, your application will receive a default web process. You can configure this behavior by changing the contents of that file. + diff --git a/docs/java_gradle/screenshots/screenshot_1.png b/docs/java_gradle/screenshots/screenshot_1.png new file mode 100644 index 0000000..4ed926c Binary files /dev/null and b/docs/java_gradle/screenshots/screenshot_1.png differ diff --git a/docs/java_maven/.gitignore b/docs/java_maven/.gitignore new file mode 100644 index 0000000..eba1228 --- /dev/null +++ b/docs/java_maven/.gitignore @@ -0,0 +1 @@ +java-getting-started diff --git a/docs/java_maven/README.md b/docs/java_maven/README.md new file mode 100644 index 0000000..045cc29 --- /dev/null +++ b/docs/java_maven/README.md @@ -0,0 +1,411 @@ + + +# Heroku Java Cloud Native Buildpack (CNB) Tutorial + +Build a Java (Maven) application image in 5 minutes, no Dockerfile required. + +At the end of this tutorial, you'll have a working [OCI image](https://opencontainers.org/) of a Java (Maven) application that can run locally. You will learn about the Cloud Native Buildpack (CNB) ecosystem, and how to utilize the [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/) to build images without the need to write or maintain a Dockerfile. + +> You can now also use the `heroku/java` CNB on Heroku [Fir generation](https://devcenter.heroku.com/articles/generations#fir) via the [pilot program](https://hello.heroku.com/next-generation-sign-up). See the [Getting started on Heroku Fir Dev Center tutorial](https://devcenter.heroku.com/articles/getting-started-with-java-maven-fir). + + +## Install the pack CLI + +We assume you have [docker installed](https://docs.docker.com/engine/install/) and a working copy [of git](https://github.com/git-guides/install-git). Next, you will need to install the CLI tool for building CNBs, [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/). If you're on a Mac you can install it via Homebrew: + +``` +$ brew install buildpacks/tap/pack +``` + + +Ensure that `pack` is installed correctly: + +``` +$ pack --version +0.35.1+git-3a22a7f.build-6099 +``` + +## Configure the default pack builder + +Once `pack` is installed, the only configuration you'll need for this tutorial is to set a default builder: + +``` +$ pack config default-builder heroku/builder:24 +Builder 'heroku/builder:24' is now the default builder +``` + + +You can view your default builder at any time: + +``` +$ pack config default-builder +The current default builder is 'heroku/builder:24' +``` + + +> Note: The `heroku/builder:24` supports both amd64 (also known as x86) and arm64 (such as aarch64 used with newer Mac machines) architectures. If needed, you can configure the architecture for `docker` and `pack` CLIs using the `--platform` argument if needed. For example `--platform linux/amd64`. +## What is a builder? + +> [!NOTE] +> Skip ahead if you want to build the application first and get into the details later. You won't need to +> know about builders for the rest of this tutorial. + +In short, a builder is a delivery mechanism for buildpacks. A builder contains references to base images and individual buildpacks. A base image contains the operating system and system dependencies. Buildpacks are the components that will configure an image to run your application, that’s where the bulk of the logic lives and why the project is called “Cloud Native Buildpacks” and not “Cloud Native Builders.” + +You can view the contents of a builder via the command `pack builder inspect`. For example: + +``` +$ pack builder inspect heroku/builder:24 | grep Buildpacks: -m1 -A10 +Buildpacks: + ID NAME VERSION HOMEPAGE + heroku/deb-packages Heroku .deb Packages 0.0.3 https://github.com/heroku/buildpacks-deb-packages + heroku/dotnet Heroku .NET 0.1.9 https://github.com/heroku/buildpacks-dotnet + heroku/go Heroku Go 0.5.0 https://github.com/heroku/buildpacks-go + heroku/gradle Heroku Gradle 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/java Heroku Java 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/jvm Heroku OpenJDK 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/maven Heroku Maven 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/nodejs Heroku Node.js 3.4.0 https://github.com/heroku/buildpacks-nodejs + heroku/nodejs-corepack Heroku Node.js Corepack 3.4.0 https://github.com/heroku/buildpacks-nodejs +``` + + +> [!NOTE] +> Your output version numbers may differ. + +This output shows the various buildpacks that represent the different languages that are supported by this builder such as `heroku/go` and `heroku/nodejs-engine`. +## Download an example Java (Maven) application + +How do you configure a CNB? Give them an application. While Dockerfile is procedural, buildpacks, are declarative. A buildpack will determine what your application needs to function by inspecting the code on disk. + +For this example, we're using a pre-built Java (Maven) application. Download it now: + +``` +$ git clone https://github.com/heroku/java-getting-started +$ cd java-getting-started +``` + + +Verify you're in the correct directory: + +``` +$ ls +LICENSE +Procfile +README.md +app.json +mvnw +mvnw.cmd +pom.xml +src +system.properties +``` + + +This tutorial was built using the following commit SHA: + +``` +$ git log --oneline | head -n1 +a583168 Update docs for Fir (#219) +``` + +## Build the application image with the pack CLI + +Now build an image named `my-image-name` by executing the heroku builder against the application by running the +`pack build` command: + +``` +$ pack build my-image-name --path . +===> ANALYZING +Image with name "my-image-name" not found +===> DETECTING +3 of 4 buildpacks participating +heroku/jvm 6.0.4 +heroku/maven 6.0.4 +heroku/procfile 3.1.2 +===> RESTORING +Skipping buildpack layer analysis +===> BUILDING + +[Installing OpenJDK 17.0.13] + +[Installing Maven] +Maven wrapper detected, skipping installation. + +[Executing Maven] +$ ./mvnw -DskipTests clean install +Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 +[INFO] Scanning for projects... +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter-parent/3.3.5/spring-boot-starter-parent-3.3.5.pom +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter-parent/3.3.5/spring-boot-starter-parent-3.3.5.pom (13 kB at 95 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-dependencies/3.3.5/spring-boot-dependencies-3.3.5.pom +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-dependencies/3.3.5/spring-boot-dependencies-3.3.5.pom (100 kB at 1.2 MB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/activemq/activemq-bom/6.1.3/activemq-bom-6.1.3.pom +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/activemq/activemq-bom/6.1.3/activemq-bom-6.1.3.pom (7.9 kB at 175 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/activemq/artemis-bom/2.33.0/artemis-bom-2.33.0.pom +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/activemq/artemis-bom/2.33.0/artemis-bom-2.33.0.pom (9.6 kB at 155 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/activemq/artemis-project/2.33.0/artemis-project-2.33.0.pom +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/activemq/artemis-project/2.33.0/artemis-project-2.33.0.pom (64 kB at 1.4 MB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/apache/31/apache-31.pom +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/apache/31/apache-31.pom (24 kB at 547 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/assertj/assertj-bom/3.25.3/assertj-bom-3.25.3.pom +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/assertj/assertj-bom/3.25.3/assertj-bom-3.25.3.pom (3.7 kB at 30 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/io/zipkin/reporter2/zipkin-reporter-bom/3.4.2/zipkin-reporter-bom-3.4.2.pom +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/io/zipkin/reporter2/zipkin-reporter-bom/3.4.2/zipkin-reporter-bom-3.4.2.pom (6.4 kB at 133 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/io/zipkin/brave/brave-bom/6.0.3/brave-bom-6.0.3.pom +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/io/zipkin/brave/brave-bom/6.0.3/brave-bom-6.0.3.pom (11 kB at 244 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/cassandra/java-driver-bom/4.18.1/java-driver-bom-4.18.1.pom +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/cassandra/java-driver-bom/4.18.1/java-driver-bom-4.18.1.pom (5.5 kB at 124 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/glassfish/jaxb/jaxb-bom/4.0.5/jaxb-bom-4.0.5.pom +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/glassfish/jaxb/jaxb-bom/4.0.5/jaxb-bom-4.0.5.pom (12 kB at 194 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/eclipse/ee4j/project/1.0.9/project-1.0.9.pom +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/eclipse/ee4j/project/1.0.9/project-1.0.9.pom (16 kB at 323 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/groovy/groovy-bom/4.0.23/groovy-bom-4.0.23.pom +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/groovy/groovy-bom/4.0.23/groovy-bom-4.0.23.pom (27 kB at 568 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/infinispan/infinispan-bom/15.0.10.Final/infinispan-bom-15.0.10.Final.pom +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/infinispan/infinispan-bom/15.0.10.Final/infinispan-bom-15.0.10.Final.pom (18 kB at 350 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/infinispan/infinispan-build-configuration-parent/15.0.10.Final/infinispan-build-configuration-parent-15.0.10.Final.pom +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/infinispan/infinispan-build-configuration-parent/15.0.10.Final/infinispan-build-configuration-parent-15.0.10.Final.pom (19 kB at 395 kB/s) +... +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/oro/oro/2.0.8/oro-2.0.8.jar (65 kB at 107 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-archiver/4.8.0/plexus-archiver-4.8.0.jar +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/commons-collections/commons-collections/3.2.2/commons-collections-3.2.2.jar (588 kB at 937 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/commons/commons-compress/1.23.0/commons-compress-1.23.0.jar +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/velocity/velocity/1.7/velocity-1.7.jar (450 kB at 694 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/com/github/luben/zstd-jni/1.5.5-5/zstd-jni-1.5.5-5.jar +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-archiver/4.8.0/plexus-archiver-4.8.0.jar (224 kB at 334 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-io/3.4.1/plexus-io-3.4.1.jar +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/commons/commons-compress/1.23.0/commons-compress-1.23.0.jar (1.1 MB at 1.4 MB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-i18n/1.0-beta-10/plexus-i18n-1.0-beta-10.jar +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-io/3.4.1/plexus-io-3.4.1.jar (79 kB at 106 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/shared/maven-dependency-analyzer/1.13.2/maven-dependency-analyzer-1.13.2.jar +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/dom4j/dom4j/1.1/dom4j-1.1.jar (457 kB at 601 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/maven-model/3.2.5/maven-model-3.2.5.jar +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-i18n/1.0-beta-10/plexus-i18n-1.0-beta-10.jar (12 kB at 15 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/shared/maven-artifact-transfer/0.13.1/maven-artifact-transfer-0.13.1.jar +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/shared/maven-dependency-analyzer/1.13.2/maven-dependency-analyzer-1.13.2.jar (39 kB at 49 kB/s) +[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-component-annotations/2.0.0/plexus-component-annotations-2.0.0.jar +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/maven-model/3.2.5/maven-model-3.2.5.jar (161 kB at 199 kB/s) +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/shared/maven-artifact-transfer/0.13.1/maven-artifact-transfer-0.13.1.jar (159 kB at 193 kB/s) +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-component-annotations/2.0.0/plexus-component-annotations-2.0.0.jar (4.2 kB at 5.0 kB/s) +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/commons-lang/commons-lang/2.4/commons-lang-2.4.jar (262 kB at 295 kB/s) +[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/com/github/luben/zstd-jni/1.5.5-5/zstd-jni-1.5.5-5.jar (5.9 MB at 5.4 MB/s) +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 11.398 s +[INFO] Finished at: 2024-12-17T18:31:13Z +[INFO] ------------------------------------------------------------------------ + +[Discovering process types] +Procfile declares types -> web +===> EXPORTING +Adding layer 'heroku/jvm:openjdk' +Adding layer 'heroku/jvm:runtime' +Adding layer 'buildpacksio/lifecycle:launch.sbom' +Added 1/1 app layer(s) +Adding layer 'buildpacksio/lifecycle:launcher' +Adding layer 'buildpacksio/lifecycle:config' +Adding layer 'buildpacksio/lifecycle:process-types' +Adding label 'io.buildpacks.lifecycle.metadata' +Adding label 'io.buildpacks.build.metadata' +Adding label 'io.buildpacks.project.metadata' +Setting default process type 'web' +Saving my-image-name... +*** Images (8ec9e477dcd6): + my-image-name +Adding cache layer 'heroku/jvm:openjdk' +Adding cache layer 'heroku/maven:repository' +Successfully built image 'my-image-name' +``` + + +> [!NOTE] +> Your output may differ. + +Verify that you see “Successfully built image my-image-name” at the end of the output. And verify that the image is present locally: + +``` +$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" | grep my-image-name +8ec9e477dcd6 my-image-name latest +``` + +## What does `pack build` do? + +> [!NOTE] +> Skip ahead if you want to run the application first and get into the details later. + +When you run `pack build` with a builder, each buildpack runs a detection script to determine if it should be eligible to build the application. In our case the `heroku/java` buildpack found a `pom.xml` file. As a result, the buildpack has enough information to install Java dependencies. You can view a list of the buildpacks used in the build output: + + +``` +===> DETECTING +3 of 4 buildpacks participating +heroku/jvm 6.0.4 +heroku/maven 6.0.4 +heroku/procfile 3.1.2 +===> RESTORING +``` + + +After the detect phase, each buildpack will execute. Buildpacks can inspect your project, install files to disk, run commands, write environment variables, [and more](https://buildpacks.io/docs/for-buildpack-authors/). You can see some examples of that in the output above. For example, the Java buildpack installs dependencies from the `pom.xml` automatically: + +``` +$ ./mvnw -DskipTests clean install +``` + + +If you’re familiar with Dockerfile you might know that [many commands in a Dockerfile will create a layer](https://dockerlabs.collabnix.com/beginners/dockerfile/Layering-Dockerfile.html). Buildpacks also use layers, but the CNB buildpack API provides for fine grained control over what exactly is in these layers and how they’re composed. Unlike Dockerfile, all images produced by CNBs [can be rebased](https://tag-env-sustainability.cncf.io/blog/2023-12-reduce-reuse-rebase-buildpacks/#reduce-reuserebase). The CNB api also improves on many of the pitfalls outlined in the satirical article [Write a Good Dockerfile in 19 'Easy' Steps](https://jkutner.github.io/2021/04/26/write-good-dockerfile.html). +## Use the image + +Even though we used `pack` and CNBs to build our image, it can be run with your favorite tools like any other OCI image. We will be using the `docker` command line to run our image. + +By default, images will be booted into a web server configuration. You can launch the app we just built by running: + +``` +$ docker run -it --rm --env PORT=5006 -p 5006:5006 my-image-name +Picked up JAVA_TOOL_OPTIONS: -XX:MaxRAMPercentage=80.0 -Dfile.encoding=UTF-8 + _ _ _ + | | | | | | + | |__| | ___ _ __ ___ | | ___ _ + | __ |/ _ \ '__/ _ \| |/ / | | | + | | | | __/ | | (_) | <| |_| | + |_| |_|\___|_| \___/|_|\_\\__,_| + +:: Built with Spring Boot :: 3.3.5 + +2024-12-15T01:46:35.109Z INFO 1 --- [ main] c.heroku.java.GettingStartedApplication : Starting GettingStartedApplication v1.0.0-SNAPSHOT using Java 17.0.13 with PID 1 (/workspace/target/java-getting-started-1.0.0-SNAPSHOT.jar started by heroku in /workspace) +2024-12-15T01:46:35.110Z INFO 1 --- [ main] c.heroku.java.GettingStartedApplication : No active profile set, falling back to 1 default profile: "default" +2024-12-15T01:46:35.552Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 5006 (http) +2024-12-15T01:46:35.557Z INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2024-12-15T01:46:35.557Z INFO 1 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.31] +2024-12-15T01:46:35.571Z INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2024-12-15T01:46:35.572Z INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 436 ms +2024-12-15T01:46:35.621Z INFO 1 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index +2024-12-15T01:46:35.760Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 5006 (http) with context path '/' +2024-12-15T01:46:35.768Z INFO 1 --- [ main] c.heroku.java.GettingStartedApplication : Started GettingStartedApplication in 0.808 seconds (process running for 1.007) +``` + + +Now when you visit [http://localhost:5006](http://localhost:5006) you should see a working web application: + +![Screenshot of http://localhost:5006/](screenshots/screenshot_1.png) + +Don't forget to stop the docker container when you're done. + + +Here's a quick breakdown of that command we just ran: + +- `docker run` Create and run a new container from an image. +- `-it` Makes the container interactive and allocates a TTY. +- `--rm` Automatically remove the container when it exits. +- `--env PORT=5006` Creates an environment variable named `PORT` and sets it to `5006` this is needed so the application inside the container knows what port to bind the web server. +- `-p 5006:5006` Publishes a container's port(s) to the host. This is what allows requests from your machine to be received by the container. +- `my-image-name` The name of the image you want to use for the application. + +So far, we've downloaded an application via git and run a single command `pack build` to generate an image, and then we can use that image as if it was generated via a Dockerfile via the `docker run` command. + +In addition to running the image as a web server, you can access the container's terminal interactively. In a new terminal window try running this command: + +``` +$ docker run -it --rm my-image-name bash +``` + + +Now you can inspect the container interactively. For example, you can see the files on disk with `ls`: + +``` +$ ls +LICENSE +Procfile +README.md +app.json +build_output.txt +mvnw +mvnw.cmd +pom.xml +src +system.properties +target +``` + + +And anything else you would typically do via an interactive container session. +## Image structure under the hood + +> [!NOTE] +> Skip this section if you want to try building your application with CNBs and learn about container structure later. + +If you’re an advanced `Dockerfile` user you might be interested in learning more about the internal structure of the image on disk. You can access the image disk interactively by using the `bash` docker command above. + +If you view the root directory `/` you’ll see there is a `layers` folder. Every buildpack that executes gets a unique folder: + +``` +$ docker run --rm my-image-name "ls /layers" +config +heroku_jvm +sbom +``` + + +Individual buildpacks can compose multiple layers from their buildpack directory. For example you can see that `java` binary is present within that buildpack layer directory: + +``` +$ docker run --rm my-image-name "which java" +/layers/heroku_jvm/openjdk/bin/java +``` + + +OCI images are represented as sequential modifications to disk. By scoping buildpack disk modifications to their own directory, the CNB API guarantees that changes to a layer in one buildpack will not affect the contents of disk to another layer. This means that OCI images produced by CNBs are rebaseable by default, while those produced by Dockerfile are not. + +We saw before how the image booted a web server by default. This is accomplished using an entrypoint. In another terminal outside of the running container you can view that entrypoint: + +``` +$ docker inspect my-image-name | grep '"Entrypoint": \[' -A2 + "Entrypoint": [ + "/cnb/process/web" + ], +``` + + +From within the image, you can see that file on disk: + +``` +$ docker run --rm my-image-name "ls /cnb/process/" +web +``` + + +While you might not need this level of detail to build and run an application with Cloud Native Buildpacks, it is useful to understand how they’re structured if you ever want to write your own buildpack. +## Try CNBs out on your application + +So far we've learned that CNBs are a declarative interface for producing OCI images (like docker). They aim to be no to low configuration and once built, you can interact with them like any other image. + +For the next step, we encourage you to try running `pack` with the Heroku builder against your application and let us know how it went. We encourage you to share your experience by [opening a discussion](https://github.com/heroku/buildpacks/discussions) and walking us through what happened: + +- What went well? +- What could be better? +- Do you have any questions? + +We are actively working on our Cloud Native Buildpacks and want to hear about your experience. The documentation below covers some intermediate-level topics that you might find helpful. +## Configuring your web process with the Procfile + +Most buildpacks rely on existing community standards to allow you to configure your application declaratively. They can also implement custom logic based on file contents on disk or environment variables present at build time. + +The `Procfile` is a configuration file format that was [introduced by Heroku in 2011](https://devcenter.heroku.com/articles/procfile), you can now use this behavior on your CNB-powered application via the `heroku/procfile`, which like the rest of the buildpacks in our builder [is open source](https://github.com/heroku/buildpacks-procfile). The `heroku/procfile` buildpack allows you to configure your web startup process. + +This is the `Procfile` of the getting started guide: + +``` +web: java -jar target/java-getting-started-1.0.0-SNAPSHOT.jar +``` + + +By including this file and using `heroku/procfile` buildpack, your application will receive a default web process. You can configure this behavior by changing the contents of that file. + diff --git a/docs/java_maven/screenshots/screenshot_1.png b/docs/java_maven/screenshots/screenshot_1.png new file mode 100644 index 0000000..6f2d402 Binary files /dev/null and b/docs/java_maven/screenshots/screenshot_1.png differ diff --git a/docs/nodejs/.gitignore b/docs/nodejs/.gitignore new file mode 100644 index 0000000..3182dd1 --- /dev/null +++ b/docs/nodejs/.gitignore @@ -0,0 +1 @@ +node-js-getting-started diff --git a/docs/nodejs/README.md b/docs/nodejs/README.md new file mode 100644 index 0000000..af68a16 --- /dev/null +++ b/docs/nodejs/README.md @@ -0,0 +1,362 @@ + + +# Heroku Node.js Cloud Native Buildpack (CNB) Tutorial + +Build a Node.js application image in 5 minutes, no Dockerfile required. + +At the end of this tutorial, you'll have a working [OCI image](https://opencontainers.org/) of a Node.js application that can run locally. You will learn about the Cloud Native Buildpack (CNB) ecosystem, and how to utilize the [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/) to build images without the need to write or maintain a Dockerfile. + +> You can now also use the `heroku/nodejs` CNB on Heroku [Fir generation](https://devcenter.heroku.com/articles/generations#fir) via the [pilot program](https://hello.heroku.com/next-generation-sign-up). See the [Getting started on Heroku Fir Dev Center tutorial](https://devcenter.heroku.com/articles/getting-started-with-nodejs-fir). + + +## Install the pack CLI + +We assume you have [docker installed](https://docs.docker.com/engine/install/) and a working copy [of git](https://github.com/git-guides/install-git). Next, you will need to install the CLI tool for building CNBs, [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/). If you're on a Mac you can install it via Homebrew: + +``` +$ brew install buildpacks/tap/pack +``` + + +Ensure that `pack` is installed correctly: + +``` +$ pack --version +0.35.1+git-3a22a7f.build-6099 +``` + +## Configure the default pack builder + +Once `pack` is installed, the only configuration you'll need for this tutorial is to set a default builder: + +``` +$ pack config default-builder heroku/builder:24 +Builder 'heroku/builder:24' is now the default builder +``` + + +You can view your default builder at any time: + +``` +$ pack config default-builder +The current default builder is 'heroku/builder:24' +``` + + +> Note: The `heroku/builder:24` supports both amd64 (also known as x86) and arm64 (such as aarch64 used with newer Mac machines) architectures. If needed, you can configure the architecture for `docker` and `pack` CLIs using the `--platform` argument if needed. For example `--platform linux/amd64`. +## What is a builder? + +> [!NOTE] +> Skip ahead if you want to build the application first and get into the details later. You won't need to +> know about builders for the rest of this tutorial. + +In short, a builder is a delivery mechanism for buildpacks. A builder contains references to base images and individual buildpacks. A base image contains the operating system and system dependencies. Buildpacks are the components that will configure an image to run your application, that’s where the bulk of the logic lives and why the project is called “Cloud Native Buildpacks” and not “Cloud Native Builders.” + +You can view the contents of a builder via the command `pack builder inspect`. For example: + +``` +$ pack builder inspect heroku/builder:24 | grep Buildpacks: -m1 -A10 +Buildpacks: + ID NAME VERSION HOMEPAGE + heroku/deb-packages Heroku .deb Packages 0.0.3 https://github.com/heroku/buildpacks-deb-packages + heroku/dotnet Heroku .NET 0.1.9 https://github.com/heroku/buildpacks-dotnet + heroku/go Heroku Go 0.5.0 https://github.com/heroku/buildpacks-go + heroku/gradle Heroku Gradle 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/java Heroku Java 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/jvm Heroku OpenJDK 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/maven Heroku Maven 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/nodejs Heroku Node.js 3.4.0 https://github.com/heroku/buildpacks-nodejs + heroku/nodejs-corepack Heroku Node.js Corepack 3.4.0 https://github.com/heroku/buildpacks-nodejs +``` + + +> [!NOTE] +> Your output version numbers may differ. + +This output shows the various buildpacks that represent the different languages that are supported by this builder such as `heroku/go` and `heroku/nodejs-engine`. +## Download an example Node.js application + +How do you configure a CNB? Give them an application. While Dockerfile is procedural, buildpacks, are declarative. A buildpack will determine what your application needs to function by inspecting the code on disk. + +For this example, we're using a pre-built Node.js application. Download it now: + +``` +$ git clone https://github.com/heroku/node-js-getting-started +$ cd node-js-getting-started +``` + + +Verify you're in the correct directory: + +``` +$ ls +Procfile +README.md +app.json +index.js +package-lock.json +package.json +public +test.js +views +``` + + +This tutorial was built using the following commit SHA: + +``` +$ git log --oneline | head -n1 +c13fb98 Applied updates from comments in #348 (#350) +``` + +## Build the application image with the pack CLI + +Now build an image named `my-image-name` by executing the heroku builder against the application by running the +`pack build` command: + +``` +$ pack build my-image-name --path . +===> ANALYZING +Image with name "my-image-name" not found +===> DETECTING +3 of 6 buildpacks participating +heroku/nodejs-engine 3.4.0 +heroku/nodejs-npm-install 3.4.0 +heroku/procfile 3.1.2 +===> RESTORING +Skipping buildpack layer analysis +===> BUILDING + +[Heroku Node.js Engine Buildpack] + +[Checking Node.js version] +Detected Node.js version range: >=18.0.0 <19.0.0-0||>=20.0.0 <21.0.0-0||>=22.0.0 <23.0.0-0 +Resolved Node.js version: 22.12.0 + +[Installing Node.js distribution] +Downloading Node.js 22.12.0 (linux-arm64) from https://nodejs.org/download/release/v22.12.0/node-v22.12.0-linux-arm64.tar.gz +Verifying checksum +Extracting Node.js 22.12.0 (linux-arm64) +Installing Node.js 22.12.0 (linux-arm64) +Installing application metrics scripts + +# Heroku Node.js npm Install Buildpack + +- Installing node modules + - Using npm version `10.9.0` + - Creating npm cache + - Configuring npm cache directory + - Running `npm ci "--production=false"` + + npm warn config production Use `--omit=dev` instead. + + added 345 packages, and audited 346 packages in 3s + + 44 packages are looking for funding + run `npm fund` for details + + found 0 vulnerabilities + + - Done (2.839s) +- Running scripts + - No build scripts found +- Configuring default processes + - Skipping default web process (Procfile detected) +- Done (finished in 3.119s) + +[Discovering process types] +Procfile declares types -> web +===> EXPORTING +Adding layer 'heroku/nodejs-engine:dist' +Adding layer 'heroku/nodejs-engine:node_runtime_metrics' +Adding layer 'heroku/nodejs-engine:web_env' +Adding layer 'heroku/nodejs-npm-install:npm_runtime_config' +Adding layer 'buildpacksio/lifecycle:launch.sbom' +Added 1/1 app layer(s) +Adding layer 'buildpacksio/lifecycle:launcher' +Adding layer 'buildpacksio/lifecycle:config' +Adding layer 'buildpacksio/lifecycle:process-types' +Adding label 'io.buildpacks.lifecycle.metadata' +Adding label 'io.buildpacks.build.metadata' +Adding label 'io.buildpacks.project.metadata' +Setting default process type 'web' +Saving my-image-name... +*** Images (ab6365ab985d): + my-image-name +Adding cache layer 'heroku/nodejs-engine:dist' +Adding cache layer 'heroku/nodejs-npm-install:npm_cache' +Successfully built image 'my-image-name' +``` + + +> [!NOTE] +> Your output may differ. + +Verify that you see “Successfully built image my-image-name” at the end of the output. And verify that the image is present locally: + +``` +$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" | grep my-image-name +ab6365ab985d my-image-name latest +``` + +## What does `pack build` do? + +> [!NOTE] +> Skip ahead if you want to run the application first and get into the details later. + +When you run `pack build` with a builder, each buildpack runs a detection script to determine if it should be eligible to build the application. In our case the `heroku/nodejs` buildpack found a `package.json` file. As a result, the buildpack has enough information to install Node.js dependencies. You can view a list of the buildpacks used in the build output: + + +``` +===> DETECTING +3 of 6 buildpacks participating +heroku/nodejs-engine 3.4.0 +heroku/nodejs-npm-install 3.4.0 +heroku/procfile 3.1.2 +===> RESTORING +``` + + +After the detect phase, each buildpack will execute. Buildpacks can inspect your project, install files to disk, run commands, write environment variables, [and more](https://buildpacks.io/docs/for-buildpack-authors/). You can see some examples of that in the output above. For example, the Node.js buildpack installs dependencies from the `package.json` automatically: + +``` + - Running `npm ci "--production=false"` +``` + + +If you’re familiar with Dockerfile you might know that [many commands in a Dockerfile will create a layer](https://dockerlabs.collabnix.com/beginners/dockerfile/Layering-Dockerfile.html). Buildpacks also use layers, but the CNB buildpack API provides for fine grained control over what exactly is in these layers and how they’re composed. Unlike Dockerfile, all images produced by CNBs [can be rebased](https://tag-env-sustainability.cncf.io/blog/2023-12-reduce-reuse-rebase-buildpacks/#reduce-reuserebase). The CNB api also improves on many of the pitfalls outlined in the satirical article [Write a Good Dockerfile in 19 'Easy' Steps](https://jkutner.github.io/2021/04/26/write-good-dockerfile.html). +## Use the image + +Even though we used `pack` and CNBs to build our image, it can be run with your favorite tools like any other OCI image. We will be using the `docker` command line to run our image. + +By default, images will be booted into a web server configuration. You can launch the app we just built by running: + +``` +$ docker run -it --rm --env PORT=5006 -p 5006:5006 my-image-name +Listening on 5006 +``` + + +Now when you visit [http://localhost:5006](http://localhost:5006) you should see a working web application: + +![Screenshot of http://localhost:5006/](screenshots/screenshot_1.png) + +Don't forget to stop the docker container when you're done. + + +Here's a quick breakdown of that command we just ran: + +- `docker run` Create and run a new container from an image. +- `-it` Makes the container interactive and allocates a TTY. +- `--rm` Automatically remove the container when it exits. +- `--env PORT=5006` Creates an environment variable named `PORT` and sets it to `5006` this is needed so the application inside the container knows what port to bind the web server. +- `-p 5006:5006` Publishes a container's port(s) to the host. This is what allows requests from your machine to be received by the container. +- `my-image-name` The name of the image you want to use for the application. + +So far, we've downloaded an application via git and run a single command `pack build` to generate an image, and then we can use that image as if it was generated via a Dockerfile via the `docker run` command. + +In addition to running the image as a web server, you can access the container's terminal interactively. In a new terminal window try running this command: + +``` +$ docker run -it --rm my-image-name bash +``` + + +Now you can inspect the container interactively. For example, you can see the files on disk with `ls`: + +``` +$ ls +Procfile +README.md +app.json +build_output.txt +index.js +node_modules +package-lock.json +package.json +public +test.js +views +``` + + +And anything else you would typically do via an interactive container session. +## Image structure under the hood + +> [!NOTE] +> Skip this section if you want to try building your application with CNBs and learn about container structure later. + +If you’re an advanced `Dockerfile` user you might be interested in learning more about the internal structure of the image on disk. You can access the image disk interactively by using the `bash` docker command above. + +If you view the root directory `/` you’ll see there is a `layers` folder. Every buildpack that executes gets a unique folder: + +``` +$ docker run --rm my-image-name "ls /layers" +config +heroku_nodejs-engine +heroku_nodejs-npm-install +sbom +``` + + +Individual buildpacks can compose multiple layers from their buildpack directory. For example you can see that `npm` binary is present within that buildpack layer directory: + +``` +$ docker run --rm my-image-name "which npm" +/layers/heroku_nodejs-engine/dist/bin/npm +``` + + +OCI images are represented as sequential modifications to disk. By scoping buildpack disk modifications to their own directory, the CNB API guarantees that changes to a layer in one buildpack will not affect the contents of disk to another layer. This means that OCI images produced by CNBs are rebaseable by default, while those produced by Dockerfile are not. + +We saw before how the image booted a web server by default. This is accomplished using an entrypoint. In another terminal outside of the running container you can view that entrypoint: + +``` +$ docker inspect my-image-name | grep '"Entrypoint": \[' -A2 + "Entrypoint": [ + "/cnb/process/web" + ], +``` + + +From within the image, you can see that file on disk: + +``` +$ docker run --rm my-image-name "ls /cnb/process/" +web +``` + + +While you might not need this level of detail to build and run an application with Cloud Native Buildpacks, it is useful to understand how they’re structured if you ever want to write your own buildpack. +## Try CNBs out on your application + +So far we've learned that CNBs are a declarative interface for producing OCI images (like docker). They aim to be no to low configuration and once built, you can interact with them like any other image. + +For the next step, we encourage you to try running `pack` with the Heroku builder against your application and let us know how it went. We encourage you to share your experience by [opening a discussion](https://github.com/heroku/buildpacks/discussions) and walking us through what happened: + +- What went well? +- What could be better? +- Do you have any questions? + +We are actively working on our Cloud Native Buildpacks and want to hear about your experience. The documentation below covers some intermediate-level topics that you might find helpful. +## Configuring your web process with the Procfile + +Most buildpacks rely on existing community standards to allow you to configure your application declaratively. They can also implement custom logic based on file contents on disk or environment variables present at build time. + +The `Procfile` is a configuration file format that was [introduced by Heroku in 2011](https://devcenter.heroku.com/articles/procfile), you can now use this behavior on your CNB-powered application via the `heroku/procfile`, which like the rest of the buildpacks in our builder [is open source](https://github.com/heroku/buildpacks-procfile). The `heroku/procfile` buildpack allows you to configure your web startup process. + +This is the `Procfile` of the getting started guide: + +``` +web: node index.js +``` + + +By including this file and using `heroku/procfile` buildpack, your application will receive a default web process. You can configure this behavior by changing the contents of that file. + diff --git a/docs/nodejs/screenshots/screenshot_1.png b/docs/nodejs/screenshots/screenshot_1.png new file mode 100644 index 0000000..40bae84 Binary files /dev/null and b/docs/nodejs/screenshots/screenshot_1.png differ diff --git a/docs/php/.gitignore b/docs/php/.gitignore new file mode 100644 index 0000000..ee9020f --- /dev/null +++ b/docs/php/.gitignore @@ -0,0 +1 @@ +php-getting-started diff --git a/docs/php/README.md b/docs/php/README.md new file mode 100644 index 0000000..e049b0e --- /dev/null +++ b/docs/php/README.md @@ -0,0 +1,481 @@ + + +# Heroku PHP Cloud Native Buildpack (CNB) Tutorial + +Build a Slim PHP application image in 5 minutes, no Dockerfile required. + +At the end of this tutorial, you'll have a working [OCI image](https://opencontainers.org/) of a Slim PHP application that can run locally. You will learn about the Cloud Native Buildpack (CNB) ecosystem, and how to utilize the [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/) to build images without the need to write or maintain a Dockerfile. + +> You can now also use the `heroku/php` CNB on Heroku [Fir generation](https://devcenter.heroku.com/articles/generations#fir) via the [pilot program](https://hello.heroku.com/next-generation-sign-up). See the [Getting started on Heroku Fir Dev Center tutorial](https://devcenter.heroku.com/articles/getting-started-with-php-fir). + + +## Install the pack CLI + +We assume you have [docker installed](https://docs.docker.com/engine/install/) and a working copy [of git](https://github.com/git-guides/install-git). Next, you will need to install the CLI tool for building CNBs, [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/). If you're on a Mac you can install it via Homebrew: + +``` +$ brew install buildpacks/tap/pack +``` + + +Ensure that `pack` is installed correctly: + +``` +$ pack --version +0.35.1+git-3a22a7f.build-6099 +``` + +## Configure the default pack builder + +Once `pack` is installed, the only configuration you'll need for this tutorial is to set a default builder: + +``` +$ pack config default-builder heroku/builder:24 +Builder 'heroku/builder:24' is now the default builder +``` + + +You can view your default builder at any time: + +``` +$ pack config default-builder +The current default builder is 'heroku/builder:24' +``` + + +> Note: The `heroku/builder:24` supports both amd64 (also known as x86) and arm64 (such as aarch64 used with newer Mac machines) architectures. If needed, you can configure the architecture for `docker` and `pack` CLIs using the `--platform` argument if needed. For example `--platform linux/amd64`. +## What is a builder? + +> [!NOTE] +> Skip ahead if you want to build the application first and get into the details later. You won't need to +> know about builders for the rest of this tutorial. + +In short, a builder is a delivery mechanism for buildpacks. A builder contains references to base images and individual buildpacks. A base image contains the operating system and system dependencies. Buildpacks are the components that will configure an image to run your application, that’s where the bulk of the logic lives and why the project is called “Cloud Native Buildpacks” and not “Cloud Native Builders.” + +You can view the contents of a builder via the command `pack builder inspect`. For example: + +``` +$ pack builder inspect heroku/builder:24 | grep Buildpacks: -m1 -A10 +Buildpacks: + ID NAME VERSION HOMEPAGE + heroku/deb-packages Heroku .deb Packages 0.0.3 https://github.com/heroku/buildpacks-deb-packages + heroku/dotnet Heroku .NET 0.1.9 https://github.com/heroku/buildpacks-dotnet + heroku/go Heroku Go 0.5.0 https://github.com/heroku/buildpacks-go + heroku/gradle Heroku Gradle 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/java Heroku Java 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/jvm Heroku OpenJDK 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/maven Heroku Maven 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/nodejs Heroku Node.js 3.4.0 https://github.com/heroku/buildpacks-nodejs + heroku/nodejs-corepack Heroku Node.js Corepack 3.4.0 https://github.com/heroku/buildpacks-nodejs +``` + + +> [!NOTE] +> Your output version numbers may differ. + +This output shows the various buildpacks that represent the different languages that are supported by this builder such as `heroku/go` and `heroku/nodejs-engine`. +## Download an example Slim PHP application + +How do you configure a CNB? Give them an application. While Dockerfile is procedural, buildpacks, are declarative. A buildpack will determine what your application needs to function by inspecting the code on disk. + +For this example, we're using a pre-built Slim PHP application. Download it now: + +``` +$ git clone https://github.com/heroku/php-getting-started +$ cd php-getting-started +``` + + +Verify you're in the correct directory: + +``` +$ ls +Procfile +README.md +app.json +composer.json +composer.lock +views +web +``` + + +This tutorial was built using the following commit SHA: + +``` +$ git log --oneline | head -n1 +35953fe Fir updates (#78) +``` + +## Build the application image with the pack CLI + +Now build an image named `my-image-name` by executing the heroku builder against the application by running the +`pack build` command: + +``` +$ pack build my-image-name --path . +===> ANALYZING +Image with name "my-image-name" not found +===> DETECTING +2 of 3 buildpacks participating +heroku/php 0.2.0 +heroku/procfile 3.1.2 +===> RESTORING +Skipping buildpack layer analysis +===> BUILDING + +[Bootstrapping] + +[Preparing platform packages installation] + +[Installing platform packages] +No composer.lock file present. Updating dependencies to latest instead of installing from lock file. See https://getcomposer.org/install for more information. +Loading composer repositories with package information +Updating dependencies +Lock file operations: 26 installs, 0 updates, 0 removals + - Locking fig/http-message-util (1.1.5) + - Locking heroku-sys/composer (2.8.2) + - Locking heroku-sys/php (8.3.13) + - Locking heroku/installer-plugin (1.8.0) + - Locking laravel/serializable-closure (v1.3.7) + - Locking monolog/monolog (3.8.0) + - Locking nikic/fast-route (v1.3.0) + - Locking php-di/invoker (2.3.4) + - Locking php-di/php-di (7.0.7) + - Locking php-di/slim-bridge (3.4.1) + - Locking psr/container (2.0.2) + - Locking psr/http-factory (1.1.0) + - Locking psr/http-message (2.0) + - Locking psr/http-server-handler (1.0.2) + - Locking psr/http-server-middleware (1.0.2) + - Locking psr/log (3.0.2) + - Locking ralouphie/getallheaders (3.0.3) + - Locking slim/psr7 (1.7.0) + - Locking slim/slim (4.14.0) + - Locking slim/twig-view (3.4.1) + - Locking symfony/deprecation-contracts (v3.5.1) + - Locking symfony/polyfill-ctype (v1.31.0) + - Locking symfony/polyfill-mbstring (v1.31.0) + - Locking symfony/polyfill-php80 (v1.31.0) + - Locking symfony/polyfill-php81 (v1.31.0) + - Locking twig/twig (v3.16.0) +Writing lock file +Installing dependencies from lock file +Package operations: 26 installs, 0 updates, 0 removals + - Installing heroku/installer-plugin (1.8.0): Mirroring from /layers/heroku_php/bootstrap_installer/support/installer + - Downloading heroku-sys/php (8.3.13) + - Downloading heroku-sys/composer (2.8.2) + 0/2 [>---------------------------] 0% + 1/2 [==============>-------------] 50% + 2/2 [============================] 100% + - Installing heroku-sys/php (8.3.13) + - Installing fig/http-message-util (1.1.5) + - Installing heroku-sys/composer (2.8.2) + - Installing laravel/serializable-closure (v1.3.7) + - Installing monolog/monolog (3.8.0) + - Installing nikic/fast-route (v1.3.0) + - Installing php-di/invoker (2.3.4) + - Installing php-di/php-di (7.0.7) + - Installing php-di/slim-bridge (3.4.1) + - Installing psr/container (2.0.2) + - Installing psr/http-factory (1.1.0) + - Installing psr/http-message (2.0) + - Installing psr/http-server-handler (1.0.2) + - Installing psr/http-server-middleware (1.0.2) + - Installing psr/log (3.0.2) + - Installing ralouphie/getallheaders (3.0.3) + - Installing slim/psr7 (1.7.0) + - Installing slim/slim (4.14.0) + - Installing slim/twig-view (3.4.1) + - Installing symfony/deprecation-contracts (v3.5.1) + - Installing symfony/polyfill-ctype (v1.31.0) + - Installing symfony/polyfill-mbstring (v1.31.0) + - Installing symfony/polyfill-php80 (v1.31.0) + - Installing symfony/polyfill-php81 (v1.31.0) + - Installing twig/twig (v3.16.0) + 0 [>---------------------------] 0 [->--------------------------] +Generating autoload files + +[Installing web servers] +No composer.lock file present. Updating dependencies to latest instead of installing from lock file. See https://getcomposer.org/install for more information. +Loading composer repositories with package information +Updating dependencies +Lock file operations: 4 installs, 0 updates, 0 removals + - Locking heroku-sys/apache (2.4.62) + - Locking heroku-sys/nginx (1.26.2) + - Locking heroku/heroku-buildpack-php (dev-bundled) + - Locking heroku/installer-plugin (1.8.0) +Writing lock file +Installing dependencies from lock file +Package operations: 4 installs, 0 updates, 0 removals + - Installing heroku/installer-plugin (1.8.0): Mirroring from /layers/heroku_php/bootstrap_installer/support/installer + - Downloading heroku-sys/apache (2.4.62) + - Downloading heroku-sys/nginx (1.26.2) + 0/2 [>---------------------------] 0% + 2/2 [============================] 100% + - Installing heroku-sys/apache (2.4.62) + - Installing heroku-sys/nginx (1.26.2) + - Installing heroku/heroku-buildpack-php (dev-bundled): Mirroring from /layers/heroku_php/bootstrap_installer + 0 [>---------------------------] 0 [->--------------------------] +Generating autoload files + +[Installing dependencies] +Installing dependencies from lock file +Verifying lock file contents can be installed on current platform. +Dependency resolution completed in 0.000 seconds +Package operations: 23 installs, 0 updates, 0 removals +Installs: laravel/serializable-closure:v1.3.7, psr/log:3.0.2, monolog/monolog:3.8.0, nikic/fast-route:v1.3.0, psr/http-message:2.0, psr/http-server-handler:1.0.2, psr/http-server-middleware:1.0.2, psr/http-factory:1.1.0, psr/container:2.0.2, slim/slim:4.14.0, php-di/invoker:2.3.4, php-di/php-di:7.0.7, php-di/slim-bridge:3.4.1, symfony/polyfill-php80:v1.31.0, ralouphie/getallheaders:3.0.3, fig/http-message-util:1.1.5, slim/psr7:1.7.0, symfony/polyfill-php81:v1.31.0, symfony/polyfill-mbstring:v1.31.0, symfony/polyfill-ctype:v1.31.0, symfony/deprecation-contracts:v3.5.1, twig/twig:v3.16.0, slim/twig-view:3.4.1 + - Downloading laravel/serializable-closure (v1.3.7) + - Downloading psr/log (3.0.2) + - Downloading monolog/monolog (3.8.0) + - Downloading nikic/fast-route (v1.3.0) + - Downloading psr/http-message (2.0) + - Downloading psr/http-server-handler (1.0.2) + - Downloading psr/http-server-middleware (1.0.2) + - Downloading psr/http-factory (1.1.0) + - Downloading psr/container (2.0.2) + - Downloading slim/slim (4.14.0) + - Downloading php-di/invoker (2.3.4) + - Downloading php-di/php-di (7.0.7) + - Downloading php-di/slim-bridge (3.4.1) + - Downloading symfony/polyfill-php80 (v1.31.0) + - Downloading ralouphie/getallheaders (3.0.3) + - Downloading fig/http-message-util (1.1.5) + - Downloading slim/psr7 (1.7.0) + - Downloading symfony/polyfill-php81 (v1.31.0) + - Downloading symfony/polyfill-mbstring (v1.31.0) + - Downloading symfony/polyfill-ctype (v1.31.0) + - Downloading symfony/deprecation-contracts (v3.5.1) + - Downloading twig/twig (v3.16.0) + - Downloading slim/twig-view (3.4.1) + - Installing laravel/serializable-closure (v1.3.7): Extracting archive + - Installing psr/log (3.0.2): Extracting archive + - Installing monolog/monolog (3.8.0): Extracting archive + - Installing nikic/fast-route (v1.3.0): Extracting archive + - Installing psr/http-message (2.0): Extracting archive + - Installing psr/http-server-handler (1.0.2): Extracting archive + - Installing psr/http-server-middleware (1.0.2): Extracting archive + - Installing psr/http-factory (1.1.0): Extracting archive + - Installing psr/container (2.0.2): Extracting archive + - Installing slim/slim (4.14.0): Extracting archive + - Installing php-di/invoker (2.3.4): Extracting archive + - Installing php-di/php-di (7.0.7): Extracting archive + - Installing php-di/slim-bridge (3.4.1): Extracting archive + - Installing symfony/polyfill-php80 (v1.31.0): Extracting archive + - Installing ralouphie/getallheaders (3.0.3): Extracting archive + - Installing fig/http-message-util (1.1.5): Extracting archive + - Installing slim/psr7 (1.7.0): Extracting archive + - Installing symfony/polyfill-php81 (v1.31.0): Extracting archive + - Installing symfony/polyfill-mbstring (v1.31.0): Extracting archive + - Installing symfony/polyfill-ctype (v1.31.0): Extracting archive + - Installing symfony/deprecation-contracts (v3.5.1): Extracting archive + - Installing twig/twig (v3.16.0): Extracting archive + - Installing slim/twig-view (3.4.1): Extracting archive +Generating optimized autoload files +10 packages you are using are looking for funding. +Use the `composer fund` command to find out more! + +[Preparing Composer runtime environment] + +[Discovering process types] +Procfile declares types -> web +===> EXPORTING +Adding layer 'heroku/php:composer_env' +Adding layer 'heroku/php:platform' +Adding layer 'heroku/php:webservers' +Adding layer 'buildpacksio/lifecycle:launch.sbom' +Added 1/1 app layer(s) +Adding layer 'buildpacksio/lifecycle:launcher' +Adding layer 'buildpacksio/lifecycle:config' +Adding layer 'buildpacksio/lifecycle:process-types' +Adding label 'io.buildpacks.lifecycle.metadata' +Adding label 'io.buildpacks.build.metadata' +Adding label 'io.buildpacks.project.metadata' +Setting default process type 'web' +Saving my-image-name... +*** Images (ecc553ef1a98): + my-image-name +Adding cache layer 'heroku/php:composer_cache' +Adding cache layer 'heroku/php:platform_cache' +Successfully built image 'my-image-name' +``` + + +> [!NOTE] +> Your output may differ. + +Verify that you see “Successfully built image my-image-name” at the end of the output. And verify that the image is present locally: + +``` +$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" | grep my-image-name +ecc553ef1a98 my-image-name latest +``` + +## What does `pack build` do? + +> [!NOTE] +> Skip ahead if you want to run the application first and get into the details later. + +When you run `pack build` with a builder, each buildpack runs a detection script to determine if it should be eligible to build the application. In our case the `heroku/php` buildpack found a `composer.json` file. As a result, the buildpack has enough information to install PHP dependencies. You can view a list of the buildpacks used in the build output: + + +``` +===> DETECTING +2 of 3 buildpacks participating +heroku/php 0.2.0 +heroku/procfile 3.1.2 +===> RESTORING +Skipping buildpack layer analysis +``` + + +After the detect phase, each buildpack will execute. Buildpacks can inspect your project, install files to disk, run commands, write environment variables, [and more](https://buildpacks.io/docs/for-buildpack-authors/). You can see some examples of that in the output above. For example, the PHP buildpack installs dependencies from the `composer.json` automatically: + +``` +[Installing dependencies] +``` + + +If you’re familiar with Dockerfile you might know that [many commands in a Dockerfile will create a layer](https://dockerlabs.collabnix.com/beginners/dockerfile/Layering-Dockerfile.html). Buildpacks also use layers, but the CNB buildpack API provides for fine grained control over what exactly is in these layers and how they’re composed. Unlike Dockerfile, all images produced by CNBs [can be rebased](https://tag-env-sustainability.cncf.io/blog/2023-12-reduce-reuse-rebase-buildpacks/#reduce-reuserebase). The CNB api also improves on many of the pitfalls outlined in the satirical article [Write a Good Dockerfile in 19 'Easy' Steps](https://jkutner.github.io/2021/04/26/write-good-dockerfile.html). +## Use the image + +Even though we used `pack` and CNBs to build our image, it can be run with your favorite tools like any other OCI image. We will be using the `docker` command line to run our image. + +By default, images will be booted into a web server configuration. You can launch the app we just built by running: + +``` +$ docker run -it --rm --env PORT=5006 -p 5006:5006 my-image-name +DOCUMENT_ROOT changed to 'web/' +Assuming RAM to be 512M Bytes +Available RAM is 512M Bytes +Number of CPU cores is 12 +PHP memory_limit is 128M Bytes +Calculated number of workers based on RAM and CPU cores is 48 +Maximum number of workers that fit available RAM at memory_limit is 4 +Limiting number of workers to 4 +Starting php-fpm with 4 workers... +Starting httpd... +Application ready for connections on port 5006. +``` + + +Now when you visit [http://localhost:5006](http://localhost:5006) you should see a working web application: + +![Screenshot of http://localhost:5006/](screenshots/screenshot_1.png) + +Don't forget to stop the docker container when you're done. + + +Here's a quick breakdown of that command we just ran: + +- `docker run` Create and run a new container from an image. +- `-it` Makes the container interactive and allocates a TTY. +- `--rm` Automatically remove the container when it exits. +- `--env PORT=5006` Creates an environment variable named `PORT` and sets it to `5006` this is needed so the application inside the container knows what port to bind the web server. +- `-p 5006:5006` Publishes a container's port(s) to the host. This is what allows requests from your machine to be received by the container. +- `my-image-name` The name of the image you want to use for the application. + +So far, we've downloaded an application via git and run a single command `pack build` to generate an image, and then we can use that image as if it was generated via a Dockerfile via the `docker run` command. + +In addition to running the image as a web server, you can access the container's terminal interactively. In a new terminal window try running this command: + +``` +$ docker run -it --rm my-image-name bash +``` + + +Now you can inspect the container interactively. For example, you can see the files on disk with `ls`: + +``` +$ ls +Procfile +README.md +app.json +build_output.txt +composer.json +composer.lock +vendor +views +web +``` + + +And anything else you would typically do via an interactive container session. +## Image structure under the hood + +> [!NOTE] +> Skip this section if you want to try building your application with CNBs and learn about container structure later. + +If you’re an advanced `Dockerfile` user you might be interested in learning more about the internal structure of the image on disk. You can access the image disk interactively by using the `bash` docker command above. + +If you view the root directory `/` you’ll see there is a `layers` folder. Every buildpack that executes gets a unique folder: + +``` +$ docker run --rm my-image-name "ls /layers" +config +heroku_php +sbom +``` + + +Individual buildpacks can compose multiple layers from their buildpack directory. For example you can see that `php` binary is present within that buildpack layer directory: + +``` +$ docker run --rm my-image-name "which php" +/layers/heroku_php/platform/bin/php +``` + + +OCI images are represented as sequential modifications to disk. By scoping buildpack disk modifications to their own directory, the CNB API guarantees that changes to a layer in one buildpack will not affect the contents of disk to another layer. This means that OCI images produced by CNBs are rebaseable by default, while those produced by Dockerfile are not. + +We saw before how the image booted a web server by default. This is accomplished using an entrypoint. In another terminal outside of the running container you can view that entrypoint: + +``` +$ docker inspect my-image-name | grep '"Entrypoint": \[' -A2 + "Entrypoint": [ + "/cnb/process/web" + ], +``` + + +From within the image, you can see that file on disk: + +``` +$ docker run --rm my-image-name "ls /cnb/process/" +web +``` + + +While you might not need this level of detail to build and run an application with Cloud Native Buildpacks, it is useful to understand how they’re structured if you ever want to write your own buildpack. +## Try CNBs out on your application + +So far we've learned that CNBs are a declarative interface for producing OCI images (like docker). They aim to be no to low configuration and once built, you can interact with them like any other image. + +For the next step, we encourage you to try running `pack` with the Heroku builder against your application and let us know how it went. We encourage you to share your experience by [opening a discussion](https://github.com/heroku/buildpacks/discussions) and walking us through what happened: + +- What went well? +- What could be better? +- Do you have any questions? + +We are actively working on our Cloud Native Buildpacks and want to hear about your experience. The documentation below covers some intermediate-level topics that you might find helpful. +## Configuring your web process with the Procfile + +Most buildpacks rely on existing community standards to allow you to configure your application declaratively. They can also implement custom logic based on file contents on disk or environment variables present at build time. + +The `Procfile` is a configuration file format that was [introduced by Heroku in 2011](https://devcenter.heroku.com/articles/procfile), you can now use this behavior on your CNB-powered application via the `heroku/procfile`, which like the rest of the buildpacks in our builder [is open source](https://github.com/heroku/buildpacks-procfile). The `heroku/procfile` buildpack allows you to configure your web startup process. + +This is the `Procfile` of the getting started guide: + +``` +web: heroku-php-apache2 web/ +``` + + +By including this file and using `heroku/procfile` buildpack, your application will receive a default web process. You can configure this behavior by changing the contents of that file. + diff --git a/docs/php/screenshots/screenshot_1.png b/docs/php/screenshots/screenshot_1.png new file mode 100644 index 0000000..7bd738a Binary files /dev/null and b/docs/php/screenshots/screenshot_1.png differ diff --git a/docs/python/.gitignore b/docs/python/.gitignore new file mode 100644 index 0000000..4ac71b0 --- /dev/null +++ b/docs/python/.gitignore @@ -0,0 +1 @@ +python-getting-started diff --git a/docs/python/README.md b/docs/python/README.md new file mode 100644 index 0000000..b7b3549 --- /dev/null +++ b/docs/python/README.md @@ -0,0 +1,378 @@ + + +# Heroku Python Cloud Native Buildpack (CNB) Tutorial + +Build a Django application image in 5 minutes, no Dockerfile required. + +At the end of this tutorial, you'll have a working [OCI image](https://opencontainers.org/) of a Django application that can run locally. You will learn about the Cloud Native Buildpack (CNB) ecosystem, and how to utilize the [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/) to build images without the need to write or maintain a Dockerfile. + +> You can now also use the `heroku/python` CNB on Heroku [Fir generation](https://devcenter.heroku.com/articles/generations#fir) via the [pilot program](https://hello.heroku.com/next-generation-sign-up). See the [Getting started on Heroku Fir Dev Center tutorial](https://devcenter.heroku.com/articles/getting-started-with-python-fir). + + +## Install the pack CLI + +We assume you have [docker installed](https://docs.docker.com/engine/install/) and a working copy [of git](https://github.com/git-guides/install-git). Next, you will need to install the CLI tool for building CNBs, [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/). If you're on a Mac you can install it via Homebrew: + +``` +$ brew install buildpacks/tap/pack +``` + + +Ensure that `pack` is installed correctly: + +``` +$ pack --version +0.35.1+git-3a22a7f.build-6099 +``` + +## Configure the default pack builder + +Once `pack` is installed, the only configuration you'll need for this tutorial is to set a default builder: + +``` +$ pack config default-builder heroku/builder:24 +Builder 'heroku/builder:24' is now the default builder +``` + + +You can view your default builder at any time: + +``` +$ pack config default-builder +The current default builder is 'heroku/builder:24' +``` + + +> Note: The `heroku/builder:24` supports both amd64 (also known as x86) and arm64 (such as aarch64 used with newer Mac machines) architectures. If needed, you can configure the architecture for `docker` and `pack` CLIs using the `--platform` argument if needed. For example `--platform linux/amd64`. +## What is a builder? + +> [!NOTE] +> Skip ahead if you want to build the application first and get into the details later. You won't need to +> know about builders for the rest of this tutorial. + +In short, a builder is a delivery mechanism for buildpacks. A builder contains references to base images and individual buildpacks. A base image contains the operating system and system dependencies. Buildpacks are the components that will configure an image to run your application, that’s where the bulk of the logic lives and why the project is called “Cloud Native Buildpacks” and not “Cloud Native Builders.” + +You can view the contents of a builder via the command `pack builder inspect`. For example: + +``` +$ pack builder inspect heroku/builder:24 | grep Buildpacks: -m1 -A10 +Buildpacks: + ID NAME VERSION HOMEPAGE + heroku/deb-packages Heroku .deb Packages 0.0.3 https://github.com/heroku/buildpacks-deb-packages + heroku/dotnet Heroku .NET 0.1.9 https://github.com/heroku/buildpacks-dotnet + heroku/go Heroku Go 0.5.0 https://github.com/heroku/buildpacks-go + heroku/gradle Heroku Gradle 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/java Heroku Java 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/jvm Heroku OpenJDK 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/maven Heroku Maven 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/nodejs Heroku Node.js 3.4.0 https://github.com/heroku/buildpacks-nodejs + heroku/nodejs-corepack Heroku Node.js Corepack 3.4.0 https://github.com/heroku/buildpacks-nodejs +``` + + +> [!NOTE] +> Your output version numbers may differ. + +This output shows the various buildpacks that represent the different languages that are supported by this builder such as `heroku/go` and `heroku/nodejs-engine`. +## Download an example Django application + +How do you configure a CNB? Give them an application. While Dockerfile is procedural, buildpacks, are declarative. A buildpack will determine what your application needs to function by inspecting the code on disk. + +For this example, we're using a pre-built Django application. Download it now: + +``` +$ git clone https://github.com/heroku/python-getting-started +$ cd python-getting-started +``` + + +Verify you're in the correct directory: + +``` +$ ls +Procfile +Procfile.windows +README.md +app.json +gettingstarted +gunicorn.conf.py +hello +manage.py +requirements.txt +``` + + +This tutorial was built using the following commit SHA: + +``` +$ git log --oneline | head -n1 +d580c39 Configure gunicorn access log (#246) +``` + +## Build the application image with the pack CLI + +Now build an image named `my-image-name` by executing the heroku builder against the application by running the +`pack build` command: + +``` +$ pack build my-image-name --path . +===> ANALYZING +Image with name "my-image-name" not found +===> DETECTING +2 of 3 buildpacks participating +heroku/python 0.20.1 +heroku/procfile 3.1.2 +===> RESTORING +Skipping buildpack layer analysis +===> BUILDING + +[Determining Python version] +Using Python version 3.13 specified in .python-version + +[Installing Python] +Installing Python 3.13.1 + +[Installing pip] +Installing pip 24.3.1 + +[Installing dependencies using pip] +Creating virtual environment +Running 'pip install -r requirements.txt' +Collecting django<5.2,>=5.1 (from -r requirements.txt (line 1)) + Downloading Django-5.1.4-py3-none-any.whl.metadata (4.2 kB) +Collecting gunicorn<24,>=23 (from -r requirements.txt (line 2)) + Downloading gunicorn-23.0.0-py3-none-any.whl.metadata (4.4 kB) +Collecting dj-database-url<3,>=2 (from -r requirements.txt (line 3)) + Downloading dj_database_url-2.3.0-py3-none-any.whl.metadata (12 kB) +Collecting whitenoise<7,>=6 (from whitenoise[brotli]<7,>=6->-r requirements.txt (line 4)) + Downloading whitenoise-6.8.2-py3-none-any.whl.metadata (3.6 kB) +Collecting asgiref<4,>=3.8.1 (from django<5.2,>=5.1->-r requirements.txt (line 1)) + Downloading asgiref-3.8.1-py3-none-any.whl.metadata (9.3 kB) +Collecting sqlparse>=0.3.1 (from django<5.2,>=5.1->-r requirements.txt (line 1)) + Downloading sqlparse-0.5.3-py3-none-any.whl.metadata (3.9 kB) +Collecting packaging (from gunicorn<24,>=23->-r requirements.txt (line 2)) + Downloading packaging-24.2-py3-none-any.whl.metadata (3.2 kB) +Collecting typing-extensions>=3.10.0.0 (from dj-database-url<3,>=2->-r requirements.txt (line 3)) + Downloading typing_extensions-4.12.2-py3-none-any.whl.metadata (3.0 kB) +Collecting brotli (from whitenoise[brotli]<7,>=6->-r requirements.txt (line 4)) + Downloading Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (5.5 kB) +Downloading Django-5.1.4-py3-none-any.whl (8.3 MB) +Downloading gunicorn-23.0.0-py3-none-any.whl (85 kB) +Downloading dj_database_url-2.3.0-py3-none-any.whl (7.8 kB) +Downloading whitenoise-6.8.2-py3-none-any.whl (20 kB) +Downloading asgiref-3.8.1-py3-none-any.whl (23 kB) +Downloading sqlparse-0.5.3-py3-none-any.whl (44 kB) +Downloading typing_extensions-4.12.2-py3-none-any.whl (37 kB) +Downloading Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.9 MB) +Downloading packaging-24.2-py3-none-any.whl (65 kB) +Installing collected packages: brotli, whitenoise, typing-extensions, sqlparse, packaging, asgiref, gunicorn, django, dj-database-url +Successfully installed asgiref-3.8.1 brotli-1.1.0 dj-database-url-2.3.0 django-5.1.4 gunicorn-23.0.0 packaging-24.2 sqlparse-0.5.3 typing-extensions-4.12.2 whitenoise-6.8.2 + +[Generating Django static files] +Running 'manage.py collectstatic' + +1 static file symlinked to '/workspace/staticfiles', 1 post-processed. + +[Discovering process types] +Procfile declares types -> web +===> EXPORTING +Adding layer 'heroku/python:python' +Adding layer 'heroku/python:venv' +Adding layer 'buildpacksio/lifecycle:launch.sbom' +Added 1/1 app layer(s) +Adding layer 'buildpacksio/lifecycle:launcher' +Adding layer 'buildpacksio/lifecycle:config' +Adding layer 'buildpacksio/lifecycle:process-types' +Adding label 'io.buildpacks.lifecycle.metadata' +Adding label 'io.buildpacks.build.metadata' +Adding label 'io.buildpacks.project.metadata' +Setting default process type 'web' +Saving my-image-name... +*** Images (415ff36d098a): + my-image-name +Adding cache layer 'heroku/python:pip' +Adding cache layer 'heroku/python:pip-cache' +Adding cache layer 'heroku/python:python' +Successfully built image 'my-image-name' +``` + + +> [!NOTE] +> Your output may differ. + +Verify that you see “Successfully built image my-image-name” at the end of the output. And verify that the image is present locally: + +``` +$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" | grep my-image-name +415ff36d098a my-image-name latest +``` + +## What does `pack build` do? + +> [!NOTE] +> Skip ahead if you want to run the application first and get into the details later. + +When you run `pack build` with a builder, each buildpack runs a detection script to determine if it should be eligible to build the application. In our case the `heroku/python` buildpack found a `requirements.txt` file. As a result, the buildpack has enough information to install Python dependencies. You can view a list of the buildpacks used in the build output: + + +``` +===> DETECTING +2 of 3 buildpacks participating +heroku/python 0.20.1 +heroku/procfile 3.1.2 +===> RESTORING +Skipping buildpack layer analysis +``` + + +After the detect phase, each buildpack will execute. Buildpacks can inspect your project, install files to disk, run commands, write environment variables, [and more](https://buildpacks.io/docs/for-buildpack-authors/). You can see some examples of that in the output above. For example, the Python buildpack installs dependencies from the `requirements.txt` automatically: + +``` +Running 'pip install -r requirements.txt' +``` + + +If you’re familiar with Dockerfile you might know that [many commands in a Dockerfile will create a layer](https://dockerlabs.collabnix.com/beginners/dockerfile/Layering-Dockerfile.html). Buildpacks also use layers, but the CNB buildpack API provides for fine grained control over what exactly is in these layers and how they’re composed. Unlike Dockerfile, all images produced by CNBs [can be rebased](https://tag-env-sustainability.cncf.io/blog/2023-12-reduce-reuse-rebase-buildpacks/#reduce-reuserebase). The CNB api also improves on many of the pitfalls outlined in the satirical article [Write a Good Dockerfile in 19 'Easy' Steps](https://jkutner.github.io/2021/04/26/write-good-dockerfile.html). +## Use the image + +Even though we used `pack` and CNBs to build our image, it can be run with your favorite tools like any other OCI image. We will be using the `docker` command line to run our image. + +By default, images will be booted into a web server configuration. You can launch the app we just built by running: + +``` +$ docker run -it --rm --env PORT=5006 -p 5006:5006 my-image-name +[2024-12-14 23:31:10 +0000] [1] [INFO] Starting gunicorn 23.0.0 +[2024-12-14 23:31:10 +0000] [1] [INFO] Listening at: http://[::]:5006 (1) +[2024-12-14 23:31:10 +0000] [1] [INFO] Using worker: gthread +[2024-12-14 23:31:10 +0000] [11] [INFO] Booting worker with pid: 11 +``` + + +Now when you visit [http://localhost:5006](http://localhost:5006) you should see a working web application: + +![Screenshot of http://localhost:5006/](screenshots/screenshot_1.png) + +Don't forget to stop the docker container when you're done. + + +Here's a quick breakdown of that command we just ran: + +- `docker run` Create and run a new container from an image. +- `-it` Makes the container interactive and allocates a TTY. +- `--rm` Automatically remove the container when it exits. +- `--env PORT=5006` Creates an environment variable named `PORT` and sets it to `5006` this is needed so the application inside the container knows what port to bind the web server. +- `-p 5006:5006` Publishes a container's port(s) to the host. This is what allows requests from your machine to be received by the container. +- `my-image-name` The name of the image you want to use for the application. + +So far, we've downloaded an application via git and run a single command `pack build` to generate an image, and then we can use that image as if it was generated via a Dockerfile via the `docker run` command. + +In addition to running the image as a web server, you can access the container's terminal interactively. In a new terminal window try running this command: + +``` +$ docker run -it --rm my-image-name bash +``` + + +Now you can inspect the container interactively. For example, you can see the files on disk with `ls`: + +``` +$ ls +Procfile +Procfile.windows +README.md +app.json +build_output.txt +gettingstarted +gunicorn.conf.py +hello +manage.py +requirements.txt +staticfiles +``` + + +And anything else you would typically do via an interactive container session. +## Image structure under the hood + +> [!NOTE] +> Skip this section if you want to try building your application with CNBs and learn about container structure later. + +If you’re an advanced `Dockerfile` user you might be interested in learning more about the internal structure of the image on disk. You can access the image disk interactively by using the `bash` docker command above. + +If you view the root directory `/` you’ll see there is a `layers` folder. Every buildpack that executes gets a unique folder: + +``` +$ docker run --rm my-image-name "ls /layers" +config +heroku_python +sbom +``` + + +Individual buildpacks can compose multiple layers from their buildpack directory. For example you can see that `python` binary is present within that buildpack layer directory: + +``` +$ docker run --rm my-image-name "which python" +/layers/heroku_python/venv/bin/python +``` + + +OCI images are represented as sequential modifications to disk. By scoping buildpack disk modifications to their own directory, the CNB API guarantees that changes to a layer in one buildpack will not affect the contents of disk to another layer. This means that OCI images produced by CNBs are rebaseable by default, while those produced by Dockerfile are not. + +We saw before how the image booted a web server by default. This is accomplished using an entrypoint. In another terminal outside of the running container you can view that entrypoint: + +``` +$ docker inspect my-image-name | grep '"Entrypoint": \[' -A2 + "Entrypoint": [ + "/cnb/process/web" + ], +``` + + +From within the image, you can see that file on disk: + +``` +$ docker run --rm my-image-name "ls /cnb/process/" +web +``` + + +While you might not need this level of detail to build and run an application with Cloud Native Buildpacks, it is useful to understand how they’re structured if you ever want to write your own buildpack. +## Try CNBs out on your application + +So far we've learned that CNBs are a declarative interface for producing OCI images (like docker). They aim to be no to low configuration and once built, you can interact with them like any other image. + +For the next step, we encourage you to try running `pack` with the Heroku builder against your application and let us know how it went. We encourage you to share your experience by [opening a discussion](https://github.com/heroku/buildpacks/discussions) and walking us through what happened: + +- What went well? +- What could be better? +- Do you have any questions? + +We are actively working on our Cloud Native Buildpacks and want to hear about your experience. The documentation below covers some intermediate-level topics that you might find helpful. +## Configuring your web process with the Procfile + +Most buildpacks rely on existing community standards to allow you to configure your application declaratively. They can also implement custom logic based on file contents on disk or environment variables present at build time. + +The `Procfile` is a configuration file format that was [introduced by Heroku in 2011](https://devcenter.heroku.com/articles/procfile), you can now use this behavior on your CNB-powered application via the `heroku/procfile`, which like the rest of the buildpacks in our builder [is open source](https://github.com/heroku/buildpacks-procfile). The `heroku/procfile` buildpack allows you to configure your web startup process. + +This is the `Procfile` of the getting started guide: + +``` +web: gunicorn --config gunicorn.conf.py gettingstarted.wsgi + +# Uncomment this `release` process if you are using a database, so that Django's model +# migrations are run as part of app deployment, using Heroku's Release Phase feature: +# https://docs.djangoproject.com/en/5.1/topics/migrations/ +# https://devcenter.heroku.com/articles/release-phase +#release: ./manage.py migrate --no-input +``` + + +By including this file and using `heroku/procfile` buildpack, your application will receive a default web process. You can configure this behavior by changing the contents of that file. + diff --git a/docs/python/screenshots/screenshot_1.png b/docs/python/screenshots/screenshot_1.png new file mode 100644 index 0000000..cda7029 Binary files /dev/null and b/docs/python/screenshots/screenshot_1.png differ diff --git a/docs/ruby/.gitignore b/docs/ruby/.gitignore new file mode 100644 index 0000000..c4dffb0 --- /dev/null +++ b/docs/ruby/.gitignore @@ -0,0 +1 @@ +ruby-getting-started diff --git a/docs/ruby/README.md b/docs/ruby/README.md index ed810b3..2a1ff51 100644 --- a/docs/ruby/README.md +++ b/docs/ruby/README.md @@ -3,14 +3,18 @@ Instead modify the rundoc script and re-run it. - Command: /home/runner/work/buildpacks/buildpacks/.rundoc-workspace/vendor/bundle/ruby/3.3.0/bin/rundoc ../docs/src/ruby/ruby_tutorial.md + Command: /Users/rschneeman/.gem/ruby/3.3.1/bin/rundoc docs/src/ruby/RUNDOC.md STOP --> + # Heroku Ruby Cloud Native Buildpack (CNB) Tutorial Build a Ruby on Rails application image in 5 minutes, no Dockerfile required. At the end of this tutorial, you'll have a working [OCI image](https://opencontainers.org/) of a Ruby on Rails application that can run locally. You will learn about the Cloud Native Buildpack (CNB) ecosystem, and how to utilize the [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/) to build images without the need to write or maintain a Dockerfile. +> You can now also use the `heroku/ruby` CNB on Heroku [Fir generation](https://devcenter.heroku.com/articles/generations#fir) via the [pilot program](https://hello.heroku.com/next-generation-sign-up). See the [Getting started on Heroku Fir Dev Center tutorial](https://devcenter.heroku.com/articles/getting-started-with-ruby-fir). + + ## Install the pack CLI We assume you have [docker installed](https://docs.docker.com/engine/install/) and a working copy [of git](https://github.com/git-guides/install-git). Next, you will need to install the CLI tool for building CNBs, [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/). If you're on a Mac you can install it via Homebrew: @@ -27,7 +31,6 @@ $ pack --version 0.35.1+git-3a22a7f.build-6099 ``` - ## Configure the default pack builder Once `pack` is installed, the only configuration you'll need for this tutorial is to set a default builder: @@ -47,7 +50,6 @@ The current default builder is 'heroku/builder:24' > Note: The `heroku/builder:24` supports both amd64 (also known as x86) and arm64 (such as aarch64 used with newer Mac machines) architectures. If needed, you can configure the architecture for `docker` and `pack` CLIs using the `--platform` argument if needed. For example `--platform linux/amd64`. - ## What is a builder? > [!NOTE] @@ -78,7 +80,6 @@ Buildpacks: > Your output version numbers may differ. This output shows the various buildpacks that represent the different languages that are supported by this builder such as `heroku/go` and `heroku/nodejs-engine`. - ## Download an example Ruby on Rails application How do you configure a CNB? Give them an application. While Dockerfile is procedural, buildpacks, are declarative. A buildpack will determine what your application needs to function by inspecting the code on disk. @@ -122,7 +123,6 @@ $ git log --oneline | head -n1 5e7ce01 Remove link (#171) ``` - ## Build the application image with the pack CLI Now build an image named `my-image-name` by executing the heroku builder against the application by running the @@ -135,7 +135,7 @@ Image with name "my-image-name" not found ===> DETECTING 3 of 6 buildpacks participating heroku/nodejs-engine 3.4.0 -heroku/ruby 4.0.1 +heroku/ruby 5.0.0 heroku/procfile 3.1.2 ===> RESTORING Skipping buildpack layer analysis @@ -148,10 +148,10 @@ Node.js version not specified, using 22.x Resolved Node.js version: 22.12.0 [Installing Node.js distribution] -Downloading Node.js 22.12.0 (linux-amd64) from https://nodejs.org/download/release/v22.12.0/node-v22.12.0-linux-x64.tar.gz +Downloading Node.js 22.12.0 (linux-arm64) from https://nodejs.org/download/release/v22.12.0/node-v22.12.0-linux-arm64.tar.gz Verifying checksum -Extracting Node.js 22.12.0 (linux-amd64) -Installing Node.js 22.12.0 (linux-amd64) +Extracting Node.js 22.12.0 (linux-arm64) +Installing Node.js 22.12.0 (linux-arm64) Installing application metrics scripts ## Heroku Ruby Buildpack @@ -159,22 +159,188 @@ Installing application metrics scripts - Metrics agent - Skipping install (`barnes` gem not found) - Ruby version `3.2.4` from `Gemfile.lock` - - Installing ... + - Installing .... (1.9s) - Bundler version `2.5.9` from `Gemfile.lock` - - Running `gem install bundler --version 2.5.9` ... + - Running `gem install bundler --version 2.5.9` ... (0.5s) - Bundle install gems - Running `BUNDLE_BIN="/layers/heroku_ruby/gems/bin" BUNDLE_CLEAN="1" BUNDLE_DEPLOYMENT="1" BUNDLE_GEMFILE="/workspace/Gemfile" BUNDLE_PATH="/layers/heroku_ruby/gems" BUNDLE_WITHOUT="development:test" bundle install` + Fetching gem metadata from https://rubygems.org/......... + Fetching rake 13.2.1 + Installing rake 13.2.1 + Fetching base64 0.2.0 + Fetching bigdecimal 3.1.8 + Fetching concurrent-ruby 1.2.3 + Fetching connection_pool 2.4.1 + Fetching drb 2.2.1 + Fetching minitest 5.23.0 + Fetching mutex_m 0.2.0 + Fetching builder 3.2.4 + Fetching erubi 1.12.0 + Fetching mini_portile2 2.8.6 + Fetching racc 1.7.3 + Fetching crass 1.0.6 + Installing base64 0.2.0 + Fetching rack 3.0.11 + Installing bigdecimal 3.1.8 with native extensions + Installing concurrent-ruby 1.2.3 + Fetching nio4r 2.7.3 + Installing connection_pool 2.4.1 + Fetching websocket-extensions 0.1.5 + Installing drb 2.2.1 + Fetching zeitwerk 2.6.14 + Installing minitest 5.23.0 + Fetching timeout 0.4.1 + Installing mutex_m 0.2.0 + Fetching marcel 1.0.4 + Installing builder 3.2.4 + Fetching mini_mime 1.1.5 + Installing erubi 1.12.0 + Fetching date 3.3.4 + Installing mini_portile2 2.8.6 + Fetching msgpack 1.7.2 + Installing racc 1.7.3 with native extensions + Installing crass 1.0.6 + Fetching coffee-script-source 1.12.2 + Installing rack 3.0.11 + Fetching execjs 2.8.1 + Installing nio4r 2.7.3 with native extensions + Installing websocket-extensions 0.1.5 + Fetching stringio 3.1.0 + Installing zeitwerk 2.6.14 + Fetching io-console 0.7.2 + Installing timeout 0.4.1 + Fetching webrick 1.8.1 + Installing marcel 1.0.4 + Fetching thor 1.3.1 + Installing mini_mime 1.1.5 + Fetching ffi 1.16.3 + Installing date 3.3.4 with native extensions + Installing msgpack 1.7.2 with native extensions + Installing coffee-script-source 1.12.2 + Fetching rb-fsevent 0.11.2 + Installing execjs 2.8.1 + Fetching pg 1.5.6 + Installing stringio 3.1.0 with native extensions + Fetching tilt 2.1.0 + Installing io-console 0.7.2 with native extensions + Installing webrick 1.8.1 + Fetching turbolinks-source 5.2.0 + Installing thor 1.3.1 + Fetching i18n 1.14.5 + Installing ffi 1.16.3 with native extensions + Installing rb-fsevent 0.11.2 + Fetching tzinfo 2.0.6 + Installing pg 1.5.6 with native extensions + Installing tilt 2.1.0 + Fetching rack-session 2.0.0 + Installing turbolinks-source 5.2.0 + Fetching rack-test 2.1.0 + Installing i18n 1.14.5 + Fetching sprockets 4.2.0 + Installing tzinfo 2.0.6 + Fetching websocket-driver 0.7.6 + Installing rack-session 2.0.0 + Fetching net-protocol 0.2.2 + Installing rack-test 2.1.0 + Fetching coffee-script 2.4.1 + Installing sprockets 4.2.0 + Installing websocket-driver 0.7.6 with native extensions + Fetching uglifier 4.2.0 + Installing net-protocol 0.2.2 + Fetching nokogiri 1.16.5 + Installing coffee-script 2.4.1 + Fetching rackup 2.1.0 + Installing uglifier 4.2.0 + Fetching turbolinks 5.2.1 + Fetching net-pop 0.1.2 + Fetching net-smtp 0.5.0 + Installing rackup 2.1.0 + Fetching psych 5.1.2 + Installing turbolinks 5.2.1 + Installing net-pop 0.1.2 + Installing nokogiri 1.16.5 with native extensions + Installing net-smtp 0.5.0 + Installing psych 5.1.2 with native extensions + Fetching reline 0.5.7 + Installing reline 0.5.7 + Fetching puma 6.4.2 + Installing puma 6.4.2 with native extensions + Fetching rdoc 6.6.3.1 + Installing rdoc 6.6.3.1 + Fetching sdoc 2.6.1 + Fetching irb 1.13.1 + Installing irb 1.13.1 + Installing sdoc 2.6.1 + Fetching bootsnap 1.18.3 + Installing bootsnap 1.18.3 with native extensions + Fetching activesupport 7.1.3.2 + Installing activesupport 7.1.3.2 + Fetching globalid 1.2.1 + Fetching activemodel 7.1.3.2 + Installing activemodel 7.1.3.2 + Fetching activerecord 7.1.3.2 + Installing globalid 1.2.1 + Fetching activejob 7.1.3.2 + Installing activerecord 7.1.3.2 + Installing activejob 7.1.3.2 + Fetching rb-inotify 0.10.1 + Fetching sassc 2.4.0 + Installing sassc 2.4.0 with native extensions + Installing rb-inotify 0.10.1 + Fetching listen 3.9.0 + Installing listen 3.9.0 + Fetching net-imap 0.4.11 + Installing net-imap 0.4.11 + Fetching mail 2.8.1 + Installing mail 2.8.1 + Fetching loofah 2.22.0 + Fetching rails-dom-testing 2.2.0 + Installing loofah 2.22.0 + Fetching rails-html-sanitizer 1.6.0 + Installing rails-dom-testing 2.2.0 + Installing rails-html-sanitizer 1.6.0 + Fetching actionview 7.1.3.2 + Installing actionview 7.1.3.2 + Fetching actionpack 7.1.3.2 + Fetching jbuilder 2.12.0 + Installing jbuilder 2.12.0 + Installing actionpack 7.1.3.2 + Fetching actionmailer 7.1.3.2 + Fetching sprockets-rails 3.4.2 + Fetching actioncable 7.1.3.2 + Fetching activestorage 7.1.3.2 + Fetching railties 7.1.3.2 + Installing actionmailer 7.1.3.2 + Installing actioncable 7.1.3.2 + Installing sprockets-rails 3.4.2 + Installing railties 7.1.3.2 + Installing activestorage 7.1.3.2 + Fetching coffee-rails 5.0.0 + Fetching jquery-rails 4.6.0 + Fetching sassc-rails 2.1.2 + Fetching actionmailbox 7.1.3.2 + Fetching actiontext 7.1.3.2 + Installing coffee-rails 5.0.0 + Installing sassc-rails 2.1.2 + Fetching sass-rails 6.0.0 + Installing jquery-rails 4.6.0 + Installing actionmailbox 7.1.3.2 + Installing actiontext 7.1.3.2 + Fetching rails 7.1.3.2 + Installing sass-rails 6.0.0 + Installing rails 7.1.3.2 + Bundle complete! 13 Gemfile dependencies, 83 gems now installed. Gems in the groups 'development' and 'test' were not installed. Bundled gems are installed into `/layers/heroku_ruby/gems` - - Done + - Done (46.3s) - Default process detection - - Running `bundle list` ... + - Running `bundle list` ... (0.1s) - Detected rails app (`rails` gem found) - Rake assets install - Detected rake (`rake` gem found, `Rakefile` found at `/workspace/Rakefile`) - - Running `bundle exec rake -P --trace` ... + - Running `bundle exec rake -P --trace` ... (0.7s) - Compiling assets with cache (detected `rake assets:precompile` and `rake assets:clean` via `bundle exec rake -P`) - Creating cache for /workspace/public/assets - Creating cache for /workspace/tmp/cache/assets @@ -186,14 +352,49 @@ Installing application metrics scripts ** Invoke environment (first_time) ** Execute environment ** Execute assets:precompile + I, [2024-12-17T17:45:32.643640 #9102] INFO -- : Writing /workspace/public/assets/manifest-dad05bf766af0fe3d79dd746db3c1361c0583026cdf35d6a2921bccaea835331.js + I, [2024-12-17T17:45:32.643855 #9102] INFO -- : Writing /workspace/public/assets/manifest-dad05bf766af0fe3d79dd746db3c1361c0583026cdf35d6a2921bccaea835331.js.gz + I, [2024-12-17T17:45:32.644168 #9102] INFO -- : Writing /workspace/public/assets/lang-logo-b6c7c4b6a37e9c2425ca4d54561010c0719870ae325c849de398499f1ab098a9.png + I, [2024-12-17T17:45:32.644721 #9102] INFO -- : Writing /workspace/public/assets/application-9ced36c9568ebfd1053e04ba411af767274dfcccd9807c0989f8bd17ca5e8f5b.js + I, [2024-12-17T17:45:32.644975 #9102] INFO -- : Writing /workspace/public/assets/application-9ced36c9568ebfd1053e04ba411af767274dfcccd9807c0989f8bd17ca5e8f5b.js.gz + I, [2024-12-17T17:45:32.645312 #9102] INFO -- : Writing /workspace/public/assets/welcome-27cfb9694c5e92d25d972c2b4a2d2e222ad088aef866823f772241c1db423402.js + I, [2024-12-17T17:45:32.645537 #9102] INFO -- : Writing /workspace/public/assets/welcome-27cfb9694c5e92d25d972c2b4a2d2e222ad088aef866823f772241c1db423402.js.gz + I, [2024-12-17T17:45:32.645846 #9102] INFO -- : Writing /workspace/public/assets/widgets-27cfb9694c5e92d25d972c2b4a2d2e222ad088aef866823f772241c1db423402.js + I, [2024-12-17T17:45:32.645899 #9102] INFO -- : Writing /workspace/public/assets/widgets-27cfb9694c5e92d25d972c2b4a2d2e222ad088aef866823f772241c1db423402.js.gz + I, [2024-12-17T17:45:32.646285 #9102] INFO -- : Writing /workspace/public/assets/application-776d900b9840362472b5b6b4afb9b798c78d53098a77b289b8bfc22c6d241913.css + I, [2024-12-17T17:45:32.646466 #9102] INFO -- : Writing /workspace/public/assets/application-776d900b9840362472b5b6b4afb9b798c78d53098a77b289b8bfc22c6d241913.css.gz + I, [2024-12-17T17:45:32.646635 #9102] INFO -- : Writing /workspace/public/assets/scaffolds-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css + I, [2024-12-17T17:45:32.646693 #9102] INFO -- : Writing /workspace/public/assets/scaffolds-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css.gz + I, [2024-12-17T17:45:32.646882 #9102] INFO -- : Writing /workspace/public/assets/theme-776d900b9840362472b5b6b4afb9b798c78d53098a77b289b8bfc22c6d241913.css + I, [2024-12-17T17:45:32.647220 #9102] INFO -- : Writing /workspace/public/assets/theme-776d900b9840362472b5b6b4afb9b798c78d53098a77b289b8bfc22c6d241913.css.gz + I, [2024-12-17T17:45:32.647556 #9102] INFO -- : Writing /workspace/public/assets/welcome-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css + I, [2024-12-17T17:45:32.647758 #9102] INFO -- : Writing /workspace/public/assets/welcome-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css.gz + I, [2024-12-17T17:45:32.647952 #9102] INFO -- : Writing /workspace/public/assets/widgets-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css + I, [2024-12-17T17:45:32.648225 #9102] INFO -- : Writing /workspace/public/assets/widgets-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css.gz + I, [2024-12-17T17:45:32.648288 #9102] INFO -- : Writing /workspace/public/assets/actiontext-78de0ebeae470799f9ec25fd0e20ae2d931df88c2ff9315918d1054a2fca2596.js + I, [2024-12-17T17:45:32.648333 #9102] INFO -- : Writing /workspace/public/assets/actiontext-78de0ebeae470799f9ec25fd0e20ae2d931df88c2ff9315918d1054a2fca2596.js.gz + I, [2024-12-17T17:45:32.648584 #9102] INFO -- : Writing /workspace/public/assets/actiontext.esm-328ef022563f73c1b9b45ace742bd21330da0f6bd6c1c96d352d52fc8b8857e5.js + I, [2024-12-17T17:45:32.648679 #9102] INFO -- : Writing /workspace/public/assets/actiontext.esm-328ef022563f73c1b9b45ace742bd21330da0f6bd6c1c96d352d52fc8b8857e5.js.gz + I, [2024-12-17T17:45:32.648750 #9102] INFO -- : Writing /workspace/public/assets/trix-e17a480fcb4e30c8571f0fed42dc81de5faeef93755ca30fe9623eb3f5c709e5.js + I, [2024-12-17T17:45:32.649338 #9102] INFO -- : Writing /workspace/public/assets/trix-e17a480fcb4e30c8571f0fed42dc81de5faeef93755ca30fe9623eb3f5c709e5.js.gz + I, [2024-12-17T17:45:32.649417 #9102] INFO -- : Writing /workspace/public/assets/trix-5552afe828fe79c41e53b9cc3616e9d7b8c2de1979ea62cbd663b88426ec41de.css + I, [2024-12-17T17:45:32.649773 #9102] INFO -- : Writing /workspace/public/assets/trix-5552afe828fe79c41e53b9cc3616e9d7b8c2de1979ea62cbd663b88426ec41de.css.gz + I, [2024-12-17T17:45:32.650409 #9102] INFO -- : Writing /workspace/public/assets/activestorage-503a4fe23aabfbcb752dad255f01835904e6961d5f20d1de13987a691c27d9cd.js + I, [2024-12-17T17:45:32.650624 #9102] INFO -- : Writing /workspace/public/assets/activestorage-503a4fe23aabfbcb752dad255f01835904e6961d5f20d1de13987a691c27d9cd.js.gz + I, [2024-12-17T17:45:32.650879 #9102] INFO -- : Writing /workspace/public/assets/activestorage.esm-b3f7f0a5ef90530b509c5e681c4b3ef5d5046851e5b70d57fdb45e32b039c883.js + I, [2024-12-17T17:45:32.651144 #9102] INFO -- : Writing /workspace/public/assets/activestorage.esm-b3f7f0a5ef90530b509c5e681c4b3ef5d5046851e5b70d57fdb45e32b039c883.js.gz + I, [2024-12-17T17:45:32.651198 #9102] INFO -- : Writing /workspace/public/assets/actioncable-1c7f008c6deb7b55c6878be38700ff6bf56b75444a086fa1f46e3b781365a3ea.js + I, [2024-12-17T17:45:32.651233 #9102] INFO -- : Writing /workspace/public/assets/actioncable-1c7f008c6deb7b55c6878be38700ff6bf56b75444a086fa1f46e3b781365a3ea.js.gz + I, [2024-12-17T17:45:32.651662 #9102] INFO -- : Writing /workspace/public/assets/actioncable.esm-06609b0ecaffe2ab952021b9c8df8b6c68f65fc23bee728fc678a2605e1ce132.js + I, [2024-12-17T17:45:32.652063 #9102] INFO -- : Writing /workspace/public/assets/actioncable.esm-06609b0ecaffe2ab952021b9c8df8b6c68f65fc23bee728fc678a2605e1ce132.js.gz ** Invoke assets:clean (first_time) - ** Invoke assets:environment + ** Invoke assets:environment ** Execute assets:clean - - Done + - Done (0.6s) - Storing cache for /workspace/public/assets - Storing cache for /workspace/tmp/cache/assets -- Done +- Done (finished in 50.4s) [Discovering process types] Procfile declares types -> web @@ -217,7 +418,7 @@ Adding label 'io.buildpacks.build.metadata' Adding label 'io.buildpacks.project.metadata' Setting default process type 'web' Saving my-image-name... -*** Images (abcdef012345): +*** Images (5768264512ab): my-image-name Adding cache layer 'heroku/nodejs-engine:dist' Adding cache layer 'heroku/ruby:bundler' @@ -236,23 +437,26 @@ Verify that you see “Successfully built image my-image-name” at the end of t ``` $ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" | grep my-image-name -abcdef012345 my-image-name latest +5768264512ab my-image-name latest ``` - ## What does `pack build` do? > [!NOTE] > Skip ahead if you want to run the application first and get into the details later. -When you run `pack build` with a builder, each buildpack runs a detection script to determine if it should be eligible to build the application. In our case the `heroku/ruby` buildpack found a `Gemfile.lock` file and `heroku/nodejs-engine` buildpack found a `package.json` file on disk. As a result, both buildpacks have enough information to install Ruby and Node dependencies. You can view a list of the buildpacks used in the output above: +When you run `pack build` with a builder, each buildpack runs a detection script to determine if it should be eligible to build the application. In our case the `heroku/ruby` buildpack found a `Gemfile` file. As a result, the buildpack has enough information to install Ruby dependencies. You can view a list of the buildpacks used in the build output: ``` ===> DETECTING 3 of 6 buildpacks participating heroku/nodejs-engine 3.4.0 +<<<<<<< HEAD heroku/ruby 4.0.1 +======= +heroku/ruby 5.0.0 +>>>>>>> 982cbbb (Update Ruby) heroku/procfile 3.1.2 ===> RESTORING ``` @@ -266,7 +470,6 @@ After the detect phase, each buildpack will execute. Buildpacks can inspect your If you’re familiar with Dockerfile you might know that [many commands in a Dockerfile will create a layer](https://dockerlabs.collabnix.com/beginners/dockerfile/Layering-Dockerfile.html). Buildpacks also use layers, but the CNB buildpack API provides for fine grained control over what exactly is in these layers and how they’re composed. Unlike Dockerfile, all images produced by CNBs [can be rebased](https://tag-env-sustainability.cncf.io/blog/2023-12-reduce-reuse-rebase-buildpacks/#reduce-reuserebase). The CNB api also improves on many of the pitfalls outlined in the satirical article [Write a Good Dockerfile in 19 'Easy' Steps](https://jkutner.github.io/2021/04/26/write-good-dockerfile.html). - ## Use the image Even though we used `pack` and CNBs to build our image, it can be run with your favorite tools like any other OCI image. We will be using the `docker` command line to run our image. @@ -274,7 +477,7 @@ Even though we used `pack` and CNBs to build our image, it can be run with your By default, images will be booted into a web server configuration. You can launch the app we just built by running: ``` -$ docker run -it --rm --env PORT=9292 -p 9292:9292 my-image-name +$ docker run -it --rm --env PORT=5006 -p 5006:5006 my-image-name [1] Puma starting in cluster mode... [1] * Puma version: 6.4.2 (ruby 3.2.4-p170) ("The Eagle of Durango") [1] * Min threads: 5 @@ -284,7 +487,7 @@ $ docker run -it --rm --env PORT=9292 -p 9292:9292 my-image-name [1] * Workers: 1 [1] * Restarts: (✔) hot (✖) phased [1] * Preloading application -[1] * Listening on http://[::]:9292 +[1] * Listening on http://[::]:5006 [1] Use Ctrl-C to stop [1] ! WARNING: Detected running cluster mode with 1 worker. [1] ! Running Puma in cluster mode with a single worker is often a misconfiguration. @@ -294,9 +497,9 @@ $ docker run -it --rm --env PORT=9292 -p 9292:9292 my-image-name ``` -Now when you visit [http://localhost:9292](http://localhost:9292) you should see a working web application: +Now when you visit [http://localhost:5006](http://localhost:5006) you should see a working web application: -![Screenshot of http://localhost:9292/](screenshots/screenshot_1.png) +![Screenshot of http://localhost:5006/](screenshots/screenshot_1.png) Don't forget to stop the docker container when you're done. @@ -306,8 +509,8 @@ Here's a quick breakdown of that command we just ran: - `docker run` Create and run a new container from an image. - `-it` Makes the container interactive and allocates a TTY. - `--rm` Automatically remove the container when it exits. -- `--env PORT=9292` Creates an environment variable named `PORT` and sets it to `9292` this is needed so the application inside the container knows what port to bind the web server. -- `-p 9292:9292` Publishes a container's port(s) to the host. This is what allows requests from your machine to be received by the container. +- `--env PORT=5006` Creates an environment variable named `PORT` and sets it to `5006` this is needed so the application inside the container knows what port to bind the web server. +- `-p 5006:5006` Publishes a container's port(s) to the host. This is what allows requests from your machine to be received by the container. - `my-image-name` The name of the image you want to use for the application. So far, we've downloaded an application via git and run a single command `pack build` to generate an image, and then we can use that image as if it was generated via a Dockerfile via the `docker run` command. @@ -346,7 +549,6 @@ vendor And anything else you would typically do via an interactive container session. - ## Image structure under the hood > [!NOTE] @@ -354,15 +556,18 @@ And anything else you would typically do via an interactive container session. If you’re an advanced `Dockerfile` user you might be interested in learning more about the internal structure of the image on disk. You can access the image disk interactively by using the `bash` docker command above. -If you view the root directory `/` you’ll see there is a `layers` folder. Every buildpack that executes gets a unique folder. For example: +If you view the root directory `/` you’ll see there is a `layers` folder. Every buildpack that executes gets a unique folder: ``` -$ docker run --rm my-image-name "ls /layers | grep ruby" +$ docker run --rm my-image-name "ls /layers" +config +heroku_nodejs-engine heroku_ruby +sbom ``` -Individual buildpacks can compose multiple layers from their buildpack directory. For example you can see that `ruby` binary is present within that ruby buildpack directory: +Individual buildpacks can compose multiple layers from their buildpack directory. For example you can see that `ruby` binary is present within that buildpack layer directory: ``` $ docker run --rm my-image-name "which ruby" @@ -391,7 +596,6 @@ web While you might not need this level of detail to build and run an application with Cloud Native Buildpacks, it is useful to understand how they’re structured if you ever want to write your own buildpack. - ## Try CNBs out on your application So far we've learned that CNBs are a declarative interface for producing OCI images (like docker). They aim to be no to low configuration and once built, you can interact with them like any other image. @@ -403,10 +607,9 @@ For the next step, we encourage you to try running `pack` with the Heroku builde - Do you have any questions? We are actively working on our Cloud Native Buildpacks and want to hear about your experience. The documentation below covers some intermediate-level topics that you might find helpful. - ## Configuring multiple languages -Language support is provided by individual buildpacks that are shipped with the builder. The above example uses the `heroku/ruby` buildpack which is [visible on GitHub](https://github.com/heroku/buildpacks-ruby). When you execute `pack build` with a builder, every buildpack has the opportunity to "detect" if it should execute against that project. The `heroku/ruby` buildpack looks for a `Gemfile.lock` in the root of the project and if found, knows how to detect a Ruby version and install dependencies. +Language support is provided by individual buildpacks that are shipped with the builder. The above example uses the `heroku/ruby` buildpack which is [visible on GitHub](https://github.com/heroku/buildpacks-ruby). When you execute `pack build` with a builder, every buildpack has the opportunity to "detect" if it should execute against that project. The `heroku/ruby` buildpack looks for a Gemfile file in the root of the project and if found, knows how to detect a ruby version and install dependencies. In addition to this auto-detection behavior, you can specify buildpacks through the `--buildpack` flag with the `pack` CLI or through a [project.toml](https://buildpacks.io/docs/for-app-developers/how-to/build-inputs/specify-buildpacks/) file at the root of your application. @@ -444,7 +647,7 @@ Image with name "my-image-name" not found ===> DETECTING heroku/python 0.20.1 heroku/nodejs-engine 3.4.0 -heroku/ruby 4.0.1 +heroku/ruby 5.0.0 heroku/procfile 3.1.2 ===> RESTORING Skipping buildpack layer analysis @@ -472,10 +675,10 @@ Node.js version not specified, using 22.x Resolved Node.js version: 22.12.0 [Installing Node.js distribution] -Downloading Node.js 22.12.0 (linux-amd64) from https://nodejs.org/download/release/v22.12.0/node-v22.12.0-linux-x64.tar.gz +Downloading Node.js 22.12.0 (linux-arm64) from https://nodejs.org/download/release/v22.12.0/node-v22.12.0-linux-arm64.tar.gz Verifying checksum -Extracting Node.js 22.12.0 (linux-amd64) -Installing Node.js 22.12.0 (linux-amd64) +Extracting Node.js 22.12.0 (linux-arm64) +Installing Node.js 22.12.0 (linux-arm64) Installing application metrics scripts ## Heroku Ruby Buildpack @@ -483,22 +686,188 @@ Installing application metrics scripts - Metrics agent - Skipping install (`barnes` gem not found) - Ruby version `3.2.4` from `Gemfile.lock` - - Installing ... + - Installing .... (1.8s) - Bundler version `2.5.9` from `Gemfile.lock` - - Running `gem install bundler --version 2.5.9` ... + - Running `gem install bundler --version 2.5.9` ... (0.7s) - Bundle install gems - Running `BUNDLE_BIN="/layers/heroku_ruby/gems/bin" BUNDLE_CLEAN="1" BUNDLE_DEPLOYMENT="1" BUNDLE_GEMFILE="/workspace/Gemfile" BUNDLE_PATH="/layers/heroku_ruby/gems" BUNDLE_WITHOUT="development:test" bundle install` + Fetching gem metadata from https://rubygems.org/......... + Fetching rake 13.2.1 + Installing rake 13.2.1 + Fetching base64 0.2.0 + Fetching bigdecimal 3.1.8 + Fetching concurrent-ruby 1.2.3 + Fetching connection_pool 2.4.1 + Fetching minitest 5.23.0 + Fetching mutex_m 0.2.0 + Fetching builder 3.2.4 + Fetching erubi 1.12.0 + Fetching mini_portile2 2.8.6 + Fetching racc 1.7.3 + Fetching crass 1.0.6 + Fetching drb 2.2.1 + Installing drb 2.2.1 + Fetching rack 3.0.11 + Installing concurrent-ruby 1.2.3 + Fetching nio4r 2.7.3 + Installing minitest 5.23.0 + Fetching websocket-extensions 0.1.5 + Installing base64 0.2.0 + Fetching zeitwerk 2.6.14 + Installing mutex_m 0.2.0 + Fetching timeout 0.4.1 + Installing erubi 1.12.0 + Fetching marcel 1.0.4 + Installing connection_pool 2.4.1 + Fetching mini_mime 1.1.5 + Installing builder 3.2.4 + Fetching date 3.3.4 + Installing crass 1.0.6 + Fetching msgpack 1.7.2 + Installing mini_portile2 2.8.6 + Fetching coffee-script-source 1.12.2 + Installing racc 1.7.3 with native extensions + Installing bigdecimal 3.1.8 with native extensions + Installing rack 3.0.11 + Fetching execjs 2.8.1 + Installing nio4r 2.7.3 with native extensions + Installing websocket-extensions 0.1.5 + Fetching stringio 3.1.0 + Installing zeitwerk 2.6.14 + Fetching io-console 0.7.2 + Installing timeout 0.4.1 + Fetching webrick 1.8.1 + Installing marcel 1.0.4 + Fetching thor 1.3.1 + Installing mini_mime 1.1.5 + Fetching ffi 1.16.3 + Installing date 3.3.4 with native extensions + Installing msgpack 1.7.2 with native extensions + Installing coffee-script-source 1.12.2 + Fetching rb-fsevent 0.11.2 + Installing execjs 2.8.1 + Fetching pg 1.5.6 + Fetching tilt 2.1.0 + Installing stringio 3.1.0 with native extensions + Installing io-console 0.7.2 with native extensions + Installing webrick 1.8.1 + Fetching turbolinks-source 5.2.0 + Installing thor 1.3.1 + Fetching i18n 1.14.5 + Installing ffi 1.16.3 with native extensions + Installing rb-fsevent 0.11.2 + Fetching tzinfo 2.0.6 + Installing pg 1.5.6 with native extensions + Installing tilt 2.1.0 + Fetching rack-session 2.0.0 + Installing turbolinks-source 5.2.0 + Fetching rack-test 2.1.0 + Installing i18n 1.14.5 + Fetching sprockets 4.2.0 + Installing tzinfo 2.0.6 + Fetching websocket-driver 0.7.6 + Installing rack-session 2.0.0 + Fetching net-protocol 0.2.2 + Installing rack-test 2.1.0 + Fetching coffee-script 2.4.1 + Installing sprockets 4.2.0 + Installing websocket-driver 0.7.6 with native extensions + Fetching uglifier 4.2.0 + Fetching nokogiri 1.16.5 + Installing net-protocol 0.2.2 + Fetching rackup 2.1.0 + Installing coffee-script 2.4.1 + Fetching turbolinks 5.2.1 + Installing uglifier 4.2.0 + Fetching psych 5.1.2 + Fetching net-pop 0.1.2 + Installing rackup 2.1.0 + Installing nokogiri 1.16.5 with native extensions + Fetching net-smtp 0.5.0 + Installing turbolinks 5.2.1 + Installing psych 5.1.2 with native extensions + Installing net-pop 0.1.2 + Installing net-smtp 0.5.0 + Fetching reline 0.5.7 + Installing reline 0.5.7 + Fetching puma 6.4.2 + Installing puma 6.4.2 with native extensions + Fetching rdoc 6.6.3.1 + Installing rdoc 6.6.3.1 + Fetching sdoc 2.6.1 + Fetching irb 1.13.1 + Installing sdoc 2.6.1 + Installing irb 1.13.1 + Fetching bootsnap 1.18.3 + Installing bootsnap 1.18.3 with native extensions + Fetching activesupport 7.1.3.2 + Installing activesupport 7.1.3.2 + Fetching globalid 1.2.1 + Fetching activemodel 7.1.3.2 + Installing globalid 1.2.1 + Fetching activejob 7.1.3.2 + Installing activemodel 7.1.3.2 + Fetching activerecord 7.1.3.2 + Installing activejob 7.1.3.2 + Fetching sassc 2.4.0 + Fetching rb-inotify 0.10.1 + Installing activerecord 7.1.3.2 + Installing rb-inotify 0.10.1 + Fetching listen 3.9.0 + Installing sassc 2.4.0 with native extensions + Installing listen 3.9.0 + Fetching net-imap 0.4.11 + Installing net-imap 0.4.11 + Fetching mail 2.8.1 + Installing mail 2.8.1 + Fetching loofah 2.22.0 + Fetching rails-dom-testing 2.2.0 + Installing loofah 2.22.0 + Fetching rails-html-sanitizer 1.6.0 + Installing rails-dom-testing 2.2.0 + Installing rails-html-sanitizer 1.6.0 + Fetching actionview 7.1.3.2 + Installing actionview 7.1.3.2 + Fetching jbuilder 2.12.0 + Fetching actionpack 7.1.3.2 + Installing jbuilder 2.12.0 + Installing actionpack 7.1.3.2 + Fetching activestorage 7.1.3.2 + Fetching actionmailer 7.1.3.2 + Fetching railties 7.1.3.2 + Fetching sprockets-rails 3.4.2 + Fetching actioncable 7.1.3.2 + Installing actioncable 7.1.3.2 + Installing activestorage 7.1.3.2 + Fetching actionmailbox 7.1.3.2 + Fetching actiontext 7.1.3.2 + Installing railties 7.1.3.2 + Installing actionmailer 7.1.3.2 + Fetching coffee-rails 5.0.0 + Fetching jquery-rails 4.6.0 + Installing sprockets-rails 3.4.2 + Fetching sassc-rails 2.1.2 + Installing actionmailbox 7.1.3.2 + Installing actiontext 7.1.3.2 + Fetching rails 7.1.3.2 + Installing coffee-rails 5.0.0 + Installing jquery-rails 4.6.0 + Installing sassc-rails 2.1.2 + Fetching sass-rails 6.0.0 + Installing rails 7.1.3.2 + Installing sass-rails 6.0.0 + Bundle complete! 13 Gemfile dependencies, 83 gems now installed. Gems in the groups 'development' and 'test' were not installed. Bundled gems are installed into `/layers/heroku_ruby/gems` - - Done + - Done (47.4s) - Default process detection - - Running `bundle list` ... + - Running `bundle list` ... (0.1s) - Detected rails app (`rails` gem found) - Rake assets install - Detected rake (`rake` gem found, `Rakefile` found at `/workspace/Rakefile`) - - Running `bundle exec rake -P --trace` ... + - Running `bundle exec rake -P --trace` ... (0.7s) - Compiling assets with cache (detected `rake assets:precompile` and `rake assets:clean` via `bundle exec rake -P`) - Creating cache for /workspace/public/assets - Creating cache for /workspace/tmp/cache/assets @@ -510,14 +879,49 @@ Installing application metrics scripts ** Invoke environment (first_time) ** Execute environment ** Execute assets:precompile + I, [2024-12-17T17:46:56.459013 #9150] INFO -- : Writing /workspace/public/assets/manifest-dad05bf766af0fe3d79dd746db3c1361c0583026cdf35d6a2921bccaea835331.js + I, [2024-12-17T17:46:56.459337 #9150] INFO -- : Writing /workspace/public/assets/manifest-dad05bf766af0fe3d79dd746db3c1361c0583026cdf35d6a2921bccaea835331.js.gz + I, [2024-12-17T17:46:56.459920 #9150] INFO -- : Writing /workspace/public/assets/lang-logo-b6c7c4b6a37e9c2425ca4d54561010c0719870ae325c849de398499f1ab098a9.png + I, [2024-12-17T17:46:56.460489 #9150] INFO -- : Writing /workspace/public/assets/application-9ced36c9568ebfd1053e04ba411af767274dfcccd9807c0989f8bd17ca5e8f5b.js + I, [2024-12-17T17:46:56.460635 #9150] INFO -- : Writing /workspace/public/assets/application-9ced36c9568ebfd1053e04ba411af767274dfcccd9807c0989f8bd17ca5e8f5b.js.gz + I, [2024-12-17T17:46:56.460935 #9150] INFO -- : Writing /workspace/public/assets/welcome-27cfb9694c5e92d25d972c2b4a2d2e222ad088aef866823f772241c1db423402.js + I, [2024-12-17T17:46:56.460988 #9150] INFO -- : Writing /workspace/public/assets/welcome-27cfb9694c5e92d25d972c2b4a2d2e222ad088aef866823f772241c1db423402.js.gz + I, [2024-12-17T17:46:56.461044 #9150] INFO -- : Writing /workspace/public/assets/widgets-27cfb9694c5e92d25d972c2b4a2d2e222ad088aef866823f772241c1db423402.js + I, [2024-12-17T17:46:56.461115 #9150] INFO -- : Writing /workspace/public/assets/widgets-27cfb9694c5e92d25d972c2b4a2d2e222ad088aef866823f772241c1db423402.js.gz + I, [2024-12-17T17:46:56.461366 #9150] INFO -- : Writing /workspace/public/assets/application-776d900b9840362472b5b6b4afb9b798c78d53098a77b289b8bfc22c6d241913.css + I, [2024-12-17T17:46:56.461648 #9150] INFO -- : Writing /workspace/public/assets/application-776d900b9840362472b5b6b4afb9b798c78d53098a77b289b8bfc22c6d241913.css.gz + I, [2024-12-17T17:46:56.461740 #9150] INFO -- : Writing /workspace/public/assets/scaffolds-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css + I, [2024-12-17T17:46:56.461816 #9150] INFO -- : Writing /workspace/public/assets/scaffolds-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css.gz + I, [2024-12-17T17:46:56.461866 #9150] INFO -- : Writing /workspace/public/assets/theme-776d900b9840362472b5b6b4afb9b798c78d53098a77b289b8bfc22c6d241913.css + I, [2024-12-17T17:46:56.461907 #9150] INFO -- : Writing /workspace/public/assets/theme-776d900b9840362472b5b6b4afb9b798c78d53098a77b289b8bfc22c6d241913.css.gz + I, [2024-12-17T17:46:56.461945 #9150] INFO -- : Writing /workspace/public/assets/welcome-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css + I, [2024-12-17T17:46:56.462521 #9150] INFO -- : Writing /workspace/public/assets/welcome-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css.gz + I, [2024-12-17T17:46:56.462607 #9150] INFO -- : Writing /workspace/public/assets/widgets-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css + I, [2024-12-17T17:46:56.463310 #9150] INFO -- : Writing /workspace/public/assets/widgets-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.css.gz + I, [2024-12-17T17:46:56.463393 #9150] INFO -- : Writing /workspace/public/assets/actiontext-78de0ebeae470799f9ec25fd0e20ae2d931df88c2ff9315918d1054a2fca2596.js + I, [2024-12-17T17:46:56.463447 #9150] INFO -- : Writing /workspace/public/assets/actiontext-78de0ebeae470799f9ec25fd0e20ae2d931df88c2ff9315918d1054a2fca2596.js.gz + I, [2024-12-17T17:46:56.463485 #9150] INFO -- : Writing /workspace/public/assets/actiontext.esm-328ef022563f73c1b9b45ace742bd21330da0f6bd6c1c96d352d52fc8b8857e5.js + I, [2024-12-17T17:46:56.463526 #9150] INFO -- : Writing /workspace/public/assets/actiontext.esm-328ef022563f73c1b9b45ace742bd21330da0f6bd6c1c96d352d52fc8b8857e5.js.gz + I, [2024-12-17T17:46:56.463570 #9150] INFO -- : Writing /workspace/public/assets/trix-e17a480fcb4e30c8571f0fed42dc81de5faeef93755ca30fe9623eb3f5c709e5.js + I, [2024-12-17T17:46:56.463623 #9150] INFO -- : Writing /workspace/public/assets/trix-e17a480fcb4e30c8571f0fed42dc81de5faeef93755ca30fe9623eb3f5c709e5.js.gz + I, [2024-12-17T17:46:56.463662 #9150] INFO -- : Writing /workspace/public/assets/trix-5552afe828fe79c41e53b9cc3616e9d7b8c2de1979ea62cbd663b88426ec41de.css + I, [2024-12-17T17:46:56.463963 #9150] INFO -- : Writing /workspace/public/assets/trix-5552afe828fe79c41e53b9cc3616e9d7b8c2de1979ea62cbd663b88426ec41de.css.gz + I, [2024-12-17T17:46:56.464155 #9150] INFO -- : Writing /workspace/public/assets/activestorage-503a4fe23aabfbcb752dad255f01835904e6961d5f20d1de13987a691c27d9cd.js + I, [2024-12-17T17:46:56.464815 #9150] INFO -- : Writing /workspace/public/assets/activestorage-503a4fe23aabfbcb752dad255f01835904e6961d5f20d1de13987a691c27d9cd.js.gz + I, [2024-12-17T17:46:56.465685 #9150] INFO -- : Writing /workspace/public/assets/activestorage.esm-b3f7f0a5ef90530b509c5e681c4b3ef5d5046851e5b70d57fdb45e32b039c883.js + I, [2024-12-17T17:46:56.466217 #9150] INFO -- : Writing /workspace/public/assets/activestorage.esm-b3f7f0a5ef90530b509c5e681c4b3ef5d5046851e5b70d57fdb45e32b039c883.js.gz + I, [2024-12-17T17:46:56.466276 #9150] INFO -- : Writing /workspace/public/assets/actioncable-1c7f008c6deb7b55c6878be38700ff6bf56b75444a086fa1f46e3b781365a3ea.js + I, [2024-12-17T17:46:56.466862 #9150] INFO -- : Writing /workspace/public/assets/actioncable-1c7f008c6deb7b55c6878be38700ff6bf56b75444a086fa1f46e3b781365a3ea.js.gz + I, [2024-12-17T17:46:56.466921 #9150] INFO -- : Writing /workspace/public/assets/actioncable.esm-06609b0ecaffe2ab952021b9c8df8b6c68f65fc23bee728fc678a2605e1ce132.js + I, [2024-12-17T17:46:56.467063 #9150] INFO -- : Writing /workspace/public/assets/actioncable.esm-06609b0ecaffe2ab952021b9c8df8b6c68f65fc23bee728fc678a2605e1ce132.js.gz ** Invoke assets:clean (first_time) - ** Invoke assets:environment + ** Invoke assets:environment ** Execute assets:clean - - Done + - Done (0.6s) - Storing cache for /workspace/public/assets - Storing cache for /workspace/tmp/cache/assets -- Done +- Done (finished in 51.5s) [Discovering process types] Procfile declares types -> web @@ -543,7 +947,7 @@ Adding label 'io.buildpacks.build.metadata' Adding label 'io.buildpacks.project.metadata' Setting default process type 'web' Saving my-image-name... -*** Images (abcdef012345): +*** Images (94042879483c): my-image-name Adding cache layer 'heroku/python:pip' Adding cache layer 'heroku/python:pip-cache' @@ -566,7 +970,6 @@ $ which python /layers/heroku_python/venv/bin/python ``` - ## Configuring your web process with the Procfile Most buildpacks rely on existing community standards to allow you to configure your application declaratively. They can also implement custom logic based on file contents on disk or environment variables present at build time. diff --git a/docs/ruby/screenshots/screenshot_1.png b/docs/ruby/screenshots/screenshot_1.png index 2a7dfed..f301068 100644 Binary files a/docs/ruby/screenshots/screenshot_1.png and b/docs/ruby/screenshots/screenshot_1.png differ diff --git a/docs/scala/.gitignore b/docs/scala/.gitignore new file mode 100644 index 0000000..65b8319 --- /dev/null +++ b/docs/scala/.gitignore @@ -0,0 +1 @@ +scala-getting-started diff --git a/docs/scala/README.md b/docs/scala/README.md new file mode 100644 index 0000000..4393141 --- /dev/null +++ b/docs/scala/README.md @@ -0,0 +1,381 @@ + + +# Heroku Java Cloud Native Buildpack (CNB) Tutorial + +Build a Scala application image in 5 minutes, no Dockerfile required. + +At the end of this tutorial, you'll have a working [OCI image](https://opencontainers.org/) of a Scala application that can run locally. You will learn about the Cloud Native Buildpack (CNB) ecosystem, and how to utilize the [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/) to build images without the need to write or maintain a Dockerfile. + +> You can now also use the `heroku/scala` CNB on Heroku [Fir generation](https://devcenter.heroku.com/articles/generations#fir) via the [pilot program](https://hello.heroku.com/next-generation-sign-up). See the [Getting started on Heroku Fir Dev Center tutorial](https://devcenter.heroku.com/articles/getting-started-with-scala-fir). + + +## Install the pack CLI + +We assume you have [docker installed](https://docs.docker.com/engine/install/) and a working copy [of git](https://github.com/git-guides/install-git). Next, you will need to install the CLI tool for building CNBs, [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/). If you're on a Mac you can install it via Homebrew: + +``` +$ brew install buildpacks/tap/pack +``` + + +Ensure that `pack` is installed correctly: + +``` +$ pack --version +0.35.1+git-3a22a7f.build-6099 +``` + +## Configure the default pack builder + +Once `pack` is installed, the only configuration you'll need for this tutorial is to set a default builder: + +``` +$ pack config default-builder heroku/builder:24 +Builder 'heroku/builder:24' is now the default builder +``` + + +You can view your default builder at any time: + +``` +$ pack config default-builder +The current default builder is 'heroku/builder:24' +``` + + +> Note: The `heroku/builder:24` supports both amd64 (also known as x86) and arm64 (such as aarch64 used with newer Mac machines) architectures. If needed, you can configure the architecture for `docker` and `pack` CLIs using the `--platform` argument if needed. For example `--platform linux/amd64`. +## What is a builder? + +> [!NOTE] +> Skip ahead if you want to build the application first and get into the details later. You won't need to +> know about builders for the rest of this tutorial. + +In short, a builder is a delivery mechanism for buildpacks. A builder contains references to base images and individual buildpacks. A base image contains the operating system and system dependencies. Buildpacks are the components that will configure an image to run your application, that’s where the bulk of the logic lives and why the project is called “Cloud Native Buildpacks” and not “Cloud Native Builders.” + +You can view the contents of a builder via the command `pack builder inspect`. For example: + +``` +$ pack builder inspect heroku/builder:24 | grep Buildpacks: -m1 -A10 +Buildpacks: + ID NAME VERSION HOMEPAGE + heroku/deb-packages Heroku .deb Packages 0.0.3 https://github.com/heroku/buildpacks-deb-packages + heroku/dotnet Heroku .NET 0.1.9 https://github.com/heroku/buildpacks-dotnet + heroku/go Heroku Go 0.5.0 https://github.com/heroku/buildpacks-go + heroku/gradle Heroku Gradle 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/java Heroku Java 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/jvm Heroku OpenJDK 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/maven Heroku Maven 6.0.4 https://github.com/heroku/buildpacks-jvm + heroku/nodejs Heroku Node.js 3.4.0 https://github.com/heroku/buildpacks-nodejs + heroku/nodejs-corepack Heroku Node.js Corepack 3.4.0 https://github.com/heroku/buildpacks-nodejs +``` + + +> [!NOTE] +> Your output version numbers may differ. + +This output shows the various buildpacks that represent the different languages that are supported by this builder such as `heroku/go` and `heroku/nodejs-engine`. +## Download an example Scala application + +How do you configure a CNB? Give them an application. While Dockerfile is procedural, buildpacks, are declarative. A buildpack will determine what your application needs to function by inspecting the code on disk. + +For this example, we're using a pre-built Scala application. Download it now: + +``` +$ git clone https://github.com/heroku/scala-getting-started +$ cd scala-getting-started +``` + + +Verify you're in the correct directory: + +``` +$ ls +LICENSE +Procfile +Procfile.windows +README.md +app +app.json +build.sbt +build.sc +conf +project +public +system.properties +``` + + +This tutorial was built using the following commit SHA: + +``` +$ git log --oneline | head -n1 +b16773f Update readme and index with fir info (#14) +``` + +## Build the application image with the pack CLI + +Now build an image named `my-image-name` by executing the heroku builder against the application by running the +`pack build` command: + +``` +$ pack build my-image-name --path . +===> ANALYZING +Image with name "my-image-name" not found +===> DETECTING +3 of 4 buildpacks participating +heroku/jvm 6.0.4 +heroku/sbt 6.0.4 +heroku/procfile 3.1.2 +===> RESTORING +Skipping buildpack layer analysis +===> BUILDING + +[Installing OpenJDK 17.0.13] + +[Building Scala project] +Running: sbt compile stage +Downloading sbt launcher for 1.7.2: + From https://repo1.maven.org/maven2/org/scala-sbt/sbt-launch/1.7.2/sbt-launch-1.7.2.jar + To /layers/heroku_sbt/sbt-extras/launchers/1.7.2/sbt-launch.jar +Downloading sbt launcher 1.7.2 md5 hash: + From https://repo1.maven.org/maven2/org/scala-sbt/sbt-launch/1.7.2/sbt-launch-1.7.2.jar.md5 + To /layers/heroku_sbt/sbt-extras/launchers/1.7.2/sbt-launch.jar.md5 +/layers/heroku_sbt/sbt-extras/launchers/1.7.2/sbt-launch.jar: OK +Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 +[info] [launcher] getting org.scala-sbt sbt 1.7.2 (this may take some time)... +[info] [launcher] getting Scala 2.12.16 (for sbt)... +[info] welcome to sbt 1.7.2 (Azul Systems, Inc. Java 17.0.13) +[info] loading global plugins from /layers/heroku_sbt/sbt-global/plugins +[info] compiling 1 Scala source to /layers/heroku_sbt/sbt-global/plugins/target/scala-2.12/sbt-1.0/classes ... +[info] Non-compiled module 'compiler-bridge_2.12' for Scala 2.12.16. Compiling... +[info] Compilation completed in 4.235s. +[info] done compiling +[info] loading settings for project workspace-build from plugins.sbt ... +[info] loading project definition from /workspace/project +[info] loading settings for project root from build.sbt ... +[info] __ __ +[info] \ \ ____ / /____ _ __ __ +[info] \ \ / __ \ / // __ `// / / / +[info] / / / /_/ // // /_/ // /_/ / +[info] /_/ / .___//_/ \__,_/ \__, / +[info] /_/ /____/ +[info] +[info] Version 2.8.19 running Java 17.0.13 +[info] +[info] Play is run entirely by the community. Please consider contributing and/or donating: +[info] https://www.playframework.com/sponsors +[info] +[info] Running Play on Java 17 is experimental. Tweaks are necessary: +[info] https://github.com/playframework/playframework/releases/2.8.15 +[info] +[info] compiling 8 Scala sources and 1 Java source to /workspace/target/scala-2.13/classes ... +[info] Non-compiled module 'compiler-bridge_2.13' for Scala 2.13.10. Compiling... +[info] Compilation completed in 3.975s. +[info] done compiling +[success] Total time: 12 s, completed Dec 15, 2024, 2:16:02 AM +[info] Wrote /workspace/target/scala-2.13/scala-getting-started_2.13-1.0-SNAPSHOT.pom +[success] Total time: 0 s, completed Dec 15, 2024, 2:16:02 AM + +[Discovering process types] +Procfile declares types -> web +===> EXPORTING +Adding layer 'heroku/jvm:openjdk' +Adding layer 'heroku/jvm:runtime' +Adding layer 'buildpacksio/lifecycle:launch.sbom' +Added 1/1 app layer(s) +Adding layer 'buildpacksio/lifecycle:launcher' +Adding layer 'buildpacksio/lifecycle:config' +Adding layer 'buildpacksio/lifecycle:process-types' +Adding label 'io.buildpacks.lifecycle.metadata' +Adding label 'io.buildpacks.build.metadata' +Adding label 'io.buildpacks.project.metadata' +Setting default process type 'web' +Saving my-image-name... +*** Images (7d6f9964107f): + my-image-name +Adding cache layer 'heroku/jvm:openjdk' +Adding cache layer 'heroku/sbt:coursier-home' +Adding cache layer 'heroku/sbt:ivy-home' +Adding cache layer 'heroku/sbt:sbt-boot' +Adding cache layer 'heroku/sbt:sbt-extras' +Successfully built image 'my-image-name' +``` + + +> [!NOTE] +> Your output may differ. + +Verify that you see “Successfully built image my-image-name” at the end of the output. And verify that the image is present locally: + +``` +$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" | grep my-image-name +7d6f9964107f my-image-name latest +``` + +## What does `pack build` do? + +> [!NOTE] +> Skip ahead if you want to run the application first and get into the details later. + +When you run `pack build` with a builder, each buildpack runs a detection script to determine if it should be eligible to build the application. In our case the `heroku/scala` buildpack found a `build.sbt` file. As a result, the buildpack has enough information to install Java dependencies. You can view a list of the buildpacks used in the build output: + + +``` +===> DETECTING +3 of 4 buildpacks participating +heroku/jvm 6.0.4 +heroku/sbt 6.0.4 +heroku/procfile 3.1.2 +===> RESTORING +``` + + +After the detect phase, each buildpack will execute. Buildpacks can inspect your project, install files to disk, run commands, write environment variables, [and more](https://buildpacks.io/docs/for-buildpack-authors/). You can see some examples of that in the output above. For example, the Java buildpack installs dependencies from the `build.sbt` automatically: + +``` +Running: sbt compile stage +``` + + +If you’re familiar with Dockerfile you might know that [many commands in a Dockerfile will create a layer](https://dockerlabs.collabnix.com/beginners/dockerfile/Layering-Dockerfile.html). Buildpacks also use layers, but the CNB buildpack API provides for fine grained control over what exactly is in these layers and how they’re composed. Unlike Dockerfile, all images produced by CNBs [can be rebased](https://tag-env-sustainability.cncf.io/blog/2023-12-reduce-reuse-rebase-buildpacks/#reduce-reuserebase). The CNB api also improves on many of the pitfalls outlined in the satirical article [Write a Good Dockerfile in 19 'Easy' Steps](https://jkutner.github.io/2021/04/26/write-good-dockerfile.html). +## Use the image + +Even though we used `pack` and CNBs to build our image, it can be run with your favorite tools like any other OCI image. We will be using the `docker` command line to run our image. + +By default, images will be booted into a web server configuration. You can launch the app we just built by running: + +``` +$ docker run -it --rm --env PORT=5006 -p 5006:5006 my-image-name +Picked up JAVA_TOOL_OPTIONS: -XX:MaxRAMPercentage=80.0 -Dfile.encoding=UTF-8 +2024-12-15 02:16:09 INFO  play.api.db.DefaultDBApi  Database [default] initialized +2024-12-15 02:16:09 INFO  play.api.db.HikariCPConnectionPool  Creating Pool for datasource 'default' +2024-12-15 02:16:09 INFO  play.api.Play  Application started (Prod) (no global state) +2024-12-15 02:16:10 INFO  play.core.server.AkkaHttpServer  Listening for HTTP on /[0:0:0:0:0:0:0:0]:5006 +``` + + +Now when you visit [http://localhost:5006](http://localhost:5006) you should see a working web application: + +![Screenshot of http://localhost:5006/](screenshots/screenshot_1.png) + +Don't forget to stop the docker container when you're done. + + +Here's a quick breakdown of that command we just ran: + +- `docker run` Create and run a new container from an image. +- `-it` Makes the container interactive and allocates a TTY. +- `--rm` Automatically remove the container when it exits. +- `--env PORT=5006` Creates an environment variable named `PORT` and sets it to `5006` this is needed so the application inside the container knows what port to bind the web server. +- `-p 5006:5006` Publishes a container's port(s) to the host. This is what allows requests from your machine to be received by the container. +- `my-image-name` The name of the image you want to use for the application. + +So far, we've downloaded an application via git and run a single command `pack build` to generate an image, and then we can use that image as if it was generated via a Dockerfile via the `docker run` command. + +In addition to running the image as a web server, you can access the container's terminal interactively. In a new terminal window try running this command: + +``` +$ docker run -it --rm my-image-name bash +``` + + +Now you can inspect the container interactively. For example, you can see the files on disk with `ls`: + +``` +$ ls +LICENSE +Procfile +Procfile.windows +README.md +app +app.json +build.sbt +build.sc +build_output.txt +conf +project +public +system.properties +target +``` + + +And anything else you would typically do via an interactive container session. +## Image structure under the hood + +> [!NOTE] +> Skip this section if you want to try building your application with CNBs and learn about container structure later. + +If you’re an advanced `Dockerfile` user you might be interested in learning more about the internal structure of the image on disk. You can access the image disk interactively by using the `bash` docker command above. + +If you view the root directory `/` you’ll see there is a `layers` folder. Every buildpack that executes gets a unique folder: + +``` +$ docker run --rm my-image-name "ls /layers" +config +heroku_jvm +sbom +``` + + +Individual buildpacks can compose multiple layers from their buildpack directory. For example you can see that `java` binary is present within that buildpack layer directory: + +``` +$ docker run --rm my-image-name "which java" +/layers/heroku_jvm/openjdk/bin/java +``` + + +OCI images are represented as sequential modifications to disk. By scoping buildpack disk modifications to their own directory, the CNB API guarantees that changes to a layer in one buildpack will not affect the contents of disk to another layer. This means that OCI images produced by CNBs are rebaseable by default, while those produced by Dockerfile are not. + +We saw before how the image booted a web server by default. This is accomplished using an entrypoint. In another terminal outside of the running container you can view that entrypoint: + +``` +$ docker inspect my-image-name | grep '"Entrypoint": \[' -A2 + "Entrypoint": [ + "/cnb/process/web" + ], +``` + + +From within the image, you can see that file on disk: + +``` +$ docker run --rm my-image-name "ls /cnb/process/" +web +``` + + +While you might not need this level of detail to build and run an application with Cloud Native Buildpacks, it is useful to understand how they’re structured if you ever want to write your own buildpack. +## Try CNBs out on your application + +So far we've learned that CNBs are a declarative interface for producing OCI images (like docker). They aim to be no to low configuration and once built, you can interact with them like any other image. + +For the next step, we encourage you to try running `pack` with the Heroku builder against your application and let us know how it went. We encourage you to share your experience by [opening a discussion](https://github.com/heroku/buildpacks/discussions) and walking us through what happened: + +- What went well? +- What could be better? +- Do you have any questions? + +We are actively working on our Cloud Native Buildpacks and want to hear about your experience. The documentation below covers some intermediate-level topics that you might find helpful. +## Configuring your web process with the Procfile + +Most buildpacks rely on existing community standards to allow you to configure your application declaratively. They can also implement custom logic based on file contents on disk or environment variables present at build time. + +The `Procfile` is a configuration file format that was [introduced by Heroku in 2011](https://devcenter.heroku.com/articles/procfile), you can now use this behavior on your CNB-powered application via the `heroku/procfile`, which like the rest of the buildpacks in our builder [is open source](https://github.com/heroku/buildpacks-procfile). The `heroku/procfile` buildpack allows you to configure your web startup process. + +This is the `Procfile` of the getting started guide: + +``` +web: target/universal/stage/bin/scala-getting-started -Dhttp.port=${PORT} +``` + + +By including this file and using `heroku/procfile` buildpack, your application will receive a default web process. You can configure this behavior by changing the contents of that file. + diff --git a/docs/scala/screenshots/screenshot_1.png b/docs/scala/screenshots/screenshot_1.png new file mode 100644 index 0000000..9b266f3 Binary files /dev/null and b/docs/scala/screenshots/screenshot_1.png differ diff --git a/docs/src/dotnet/RUNDOC.md b/docs/src/dotnet/RUNDOC.md new file mode 100644 index 0000000..225f69a --- /dev/null +++ b/docs/src/dotnet/RUNDOC.md @@ -0,0 +1,33 @@ +```ruby +:::-- rundoc.require "../shared/setup.md" +:::-- print.erb +<% + setup( + app_url: "https://github.com/heroku/dotnet-getting-started", + which_binary: "dotnet", # Executable on disk after buildpack i.e. `which ruby` + tutorial_type: ".NET", # Type of app "Ruby on Rails" tutorial + language_name: ".NET", # Buildpack name Heroku "Ruby" buildpack + buildpack_url: "https://github.com/heroku/buildpacks-dotnet", + buildpack_name: "heroku/dotnet", # Name of buildpack in registry + dependency_file: "Frontend.csproj", # File needed for detection + install_cmd_grep: "dotnet publish", # A command (or other string) notable in the build output + fir_tutorial_url: "https://devcenter.heroku.com/articles/getting-started-with-dotnet-fir" + ) +%> +``` + +``` +:::>> rundoc.require "../shared/intro.md" +:::>> rundoc.require "../shared/install_pack.md" +:::>> rundoc.require "../shared/configure_builder.md" +:::>> rundoc.require "../shared/what_is_a_builder.md" +:::>> rundoc.require "../shared/download.md" +:::>> rundoc.require "../shared/pack_build.md" +:::>> rundoc.require "../shared/what_is_pack_build.md" +:::>> rundoc.require "../shared/use_the_image.md" +:::>> rundoc.require "../shared/image_structure.md" +:::>> rundoc.require "../shared/call_to_action.md" +:::>> rundoc.require "./multiple_langs.md" +:::>> rundoc.require "../shared/procfile.md" +:::>> rundoc.require "../shared/cleanup.md" +``` diff --git a/docs/src/dotnet/multiple_langs.md b/docs/src/dotnet/multiple_langs.md new file mode 100644 index 0000000..dafe53a --- /dev/null +++ b/docs/src/dotnet/multiple_langs.md @@ -0,0 +1,45 @@ +```md +## Configuring multiple languages + +:::-> print.erb +Language support is provided by individual buildpacks that are shipped with the builder. The above example uses the `<%= buildpack_name %>` buildpack which is [visible on GitHub](<%= buildpack_url %>). When you execute `pack build` with a builder, every buildpack has the opportunity to "detect" if it should execute against that project. The `<%= buildpack_name %>` buildpack looks for a <% dependency_file %> file in the root of the project and if found, knows how to detect a <%= which_binary %> version and install dependencies. + +In addition to this auto-detection behavior, you can specify buildpacks through the `--buildpack` flag with the `pack` CLI or through a [project.toml](https://buildpacks.io/docs/for-app-developers/how-to/build-inputs/specify-buildpacks/) file at the root of your application. + +For example, if you wanted to install both <%= which_binary %> and Python you could create a `project.toml` file in the root of your application and specify those buildpacks. +``` + +```toml +:::>> file.write project.toml +[_] +schema-version = "0.2" +id = "sample.dotnet+python.app" +name = "Sample .NET & Python App" +version = "1.0.0" + +[[io.buildpacks.group]] +uri = "heroku/python" + +[[io.buildpacks.group]] +uri = "heroku/dotnet" + +[[io.buildpacks.group]] +uri = "heroku/procfile" +``` + +Ensure that a `requirements.txt` file, a web solution file all exist and then build your application: + +``` +$ pack build my-image-name --path . +:::-- $ touch requirements.txt +:::-- $ docker rmi -f my-image-name +:::-> $ pack build my-image-name --path . --pull-policy if-not-present --clear-cache +``` + +You can run the image and inspect everything is installed as expected: + +``` +$ docker run -it --rm my-image-name bash +$ which python +:::-> $ docker run --rm my-image-name "which python" +``` diff --git a/docs/src/go/RUNDOC.md b/docs/src/go/RUNDOC.md new file mode 100644 index 0000000..bd14b3c --- /dev/null +++ b/docs/src/go/RUNDOC.md @@ -0,0 +1,32 @@ +```ruby +:::-- rundoc.require "../shared/setup.md" +:::-- print.erb +<% + setup( + app_url: "https://github.com/heroku/go-getting-started", + which_binary: "go-getting-started", # Executable on disk after buildpack i.e. `which ruby` + tutorial_type: "Go", # Type of app "Ruby on Rails" tutorial + language_name: "Go", # Buildpack name Heroku "Ruby" buildpack + buildpack_url: "https://github.com/heroku/buildpacks-go", + buildpack_name: "heroku/go", # Name of buildpack in registry + dependency_file: "go.mod", # File needed for detection + install_cmd_grep: "\\[Building Go binaries\\]", # A command (or other string) notable in the build output + fir_tutorial_url: "https://devcenter.heroku.com/articles/getting-started-with-go-fir" + ) +%> +``` + +``` +:::>> rundoc.require "../shared/intro.md" +:::>> rundoc.require "../shared/install_pack.md" +:::>> rundoc.require "../shared/configure_builder.md" +:::>> rundoc.require "../shared/what_is_a_builder.md" +:::>> rundoc.require "../shared/download.md" +:::>> rundoc.require "../shared/pack_build.md" +:::>> rundoc.require "../shared/what_is_pack_build.md" +:::>> rundoc.require "../shared/use_the_image.md" +:::>> rundoc.require "../shared/image_structure.md" +:::>> rundoc.require "../shared/call_to_action.md" +:::>> rundoc.require "../shared/procfile.md" +:::>> rundoc.require "../shared/cleanup.md" +``` diff --git a/docs/src/java_gradle/RUNDOC.md b/docs/src/java_gradle/RUNDOC.md new file mode 100644 index 0000000..30d9b33 --- /dev/null +++ b/docs/src/java_gradle/RUNDOC.md @@ -0,0 +1,32 @@ +```ruby +:::-- rundoc.require "../shared/setup.md" +:::-- print.erb +<% + setup( + app_url: "https://github.com/heroku/gradle-getting-started", + which_binary: "java", # Executable on disk after buildpack i.e. `which ruby` + tutorial_type: "Java (Gradle)", # Type of app "Ruby on Rails" tutorial + language_name: "Java", # Buildpack name Heroku "Ruby" buildpack + buildpack_url: "https://github.com/heroku/buildpacks-jvm", + buildpack_name: "heroku/java", # Name of buildpack in registry + dependency_file: "build.gradle.kts", # File needed for detection + install_cmd_grep: "compileJava", # A command (or other string) notable in the build output + fir_tutorial_url: "https://devcenter.heroku.com/articles/getting-started-with-java-gradle-fir" + ) +%> +``` + +``` +:::>> rundoc.require "../shared/intro.md" +:::>> rundoc.require "../shared/install_pack.md" +:::>> rundoc.require "../shared/configure_builder.md" +:::>> rundoc.require "../shared/what_is_a_builder.md" +:::>> rundoc.require "../shared/download.md" +:::>> rundoc.require "../shared/pack_build.md" +:::>> rundoc.require "../shared/what_is_pack_build.md" +:::>> rundoc.require "../shared/use_the_image.md" +:::>> rundoc.require "../shared/image_structure.md" +:::>> rundoc.require "../shared/call_to_action.md" +:::>> rundoc.require "../shared/procfile.md" +:::>> rundoc.require "../shared/cleanup.md" +``` diff --git a/docs/src/java_maven/RUNDOC.md b/docs/src/java_maven/RUNDOC.md new file mode 100644 index 0000000..d13d1c0 --- /dev/null +++ b/docs/src/java_maven/RUNDOC.md @@ -0,0 +1,33 @@ +```ruby +:::-- rundoc.require "../shared/setup.md" +:::-- print.erb +<% + setup( + app_url: "https://github.com/heroku/java-getting-started", + which_binary: "java", # Executable on disk after buildpack i.e. `which ruby` + tutorial_type: "Java (Maven)", # Type of app "Ruby on Rails" tutorial + language_name: "Java", # Buildpack name Heroku "Ruby" buildpack + buildpack_url: "https://github.com/heroku/buildpacks-jvm", + buildpack_name: "heroku/java", # Name of buildpack in registry + dependency_file: "pom.xml", # File needed for detection + install_cmd_grep: "./mvnw", # A command (or other string) notable in the build output + fir_tutorial_url: "https://devcenter.heroku.com/articles/getting-started-with-java-maven-fir", + pack_build_head_tail: 50 # Number of lines to show from `pack build` + ) +%> +``` + +``` +:::>> rundoc.require "../shared/intro.md" +:::>> rundoc.require "../shared/install_pack.md" +:::>> rundoc.require "../shared/configure_builder.md" +:::>> rundoc.require "../shared/what_is_a_builder.md" +:::>> rundoc.require "../shared/download.md" +:::>> rundoc.require "../shared/pack_build.md" +:::>> rundoc.require "../shared/what_is_pack_build.md" +:::>> rundoc.require "../shared/use_the_image.md" +:::>> rundoc.require "../shared/image_structure.md" +:::>> rundoc.require "../shared/call_to_action.md" +:::>> rundoc.require "../shared/procfile.md" +:::>> rundoc.require "../shared/cleanup.md" +``` diff --git a/docs/src/nodejs/RUNDOC.md b/docs/src/nodejs/RUNDOC.md new file mode 100644 index 0000000..ad19c6b --- /dev/null +++ b/docs/src/nodejs/RUNDOC.md @@ -0,0 +1,32 @@ +```ruby +:::-- rundoc.require "../shared/setup.md" +:::-- print.erb +<% + setup( + app_url: "https://github.com/heroku/node-js-getting-started", + which_binary: "npm", # Executable on disk after buildpack i.e. `which ruby` + tutorial_type: "Node.js", # Type of app "Ruby on Rails" tutorial + language_name: "Node.js", # Buildpack name Heroku "Ruby" buildpack + buildpack_url: "https://github.com/heroku/buildpacks-nodejs", + buildpack_name: "heroku/nodejs", # Name of buildpack in registry + dependency_file: "package.json", # File needed for detection + install_cmd_grep: "npm ci", # A command (or other string) notable in the build output + fir_tutorial_url: "https://devcenter.heroku.com/articles/getting-started-with-nodejs-fir" + ) +%> +``` + +``` +:::>> rundoc.require "../shared/intro.md" +:::>> rundoc.require "../shared/install_pack.md" +:::>> rundoc.require "../shared/configure_builder.md" +:::>> rundoc.require "../shared/what_is_a_builder.md" +:::>> rundoc.require "../shared/download.md" +:::>> rundoc.require "../shared/pack_build.md" +:::>> rundoc.require "../shared/what_is_pack_build.md" +:::>> rundoc.require "../shared/use_the_image.md" +:::>> rundoc.require "../shared/image_structure.md" +:::>> rundoc.require "../shared/call_to_action.md" +:::>> rundoc.require "../shared/procfile.md" +:::>> rundoc.require "../shared/cleanup.md" +``` diff --git a/docs/src/php/RUNDOC.md b/docs/src/php/RUNDOC.md new file mode 100644 index 0000000..f0c0844 --- /dev/null +++ b/docs/src/php/RUNDOC.md @@ -0,0 +1,32 @@ +```ruby +:::-- rundoc.require "../shared/setup.md" +:::-- print.erb +<% + setup( + app_url: "https://github.com/heroku/php-getting-started", + which_binary: "php", # Executable on disk after buildpack i.e. `which ruby` + tutorial_type: "Slim PHP", # Type of app "Ruby on Rails" tutorial + language_name: "PHP", # Buildpack name Heroku "Ruby" buildpack + buildpack_url: "https://github.com/heroku/buildpacks-php", + buildpack_name: "heroku/php", # Name of buildpack in registry + dependency_file: "composer.json", # File needed for detection + install_cmd_grep: "\\[Installing dependencies\\]", # A command (or other string) notable in the build output + fir_tutorial_url: "https://devcenter.heroku.com/articles/getting-started-with-php-fir" + ) +%> +``` + +``` +:::>> rundoc.require "../shared/intro.md" +:::>> rundoc.require "../shared/install_pack.md" +:::>> rundoc.require "../shared/configure_builder.md" +:::>> rundoc.require "../shared/what_is_a_builder.md" +:::>> rundoc.require "../shared/download.md" +:::>> rundoc.require "../shared/pack_build.md" +:::>> rundoc.require "../shared/what_is_pack_build.md" +:::>> rundoc.require "../shared/use_the_image.md" +:::>> rundoc.require "../shared/image_structure.md" +:::>> rundoc.require "../shared/call_to_action.md" +:::>> rundoc.require "../shared/procfile.md" +:::>> rundoc.require "../shared/cleanup.md" +``` diff --git a/docs/src/python/RUNDOC.md b/docs/src/python/RUNDOC.md new file mode 100644 index 0000000..34ae7f4 --- /dev/null +++ b/docs/src/python/RUNDOC.md @@ -0,0 +1,32 @@ +```ruby +:::-- rundoc.require "../shared/setup.md" +:::-- print.erb +<% + setup( + app_url: "https://github.com/heroku/python-getting-started", + which_binary: "python", # Executable on disk after buildpack i.e. `which ruby` + tutorial_type: "Django", # Type of app "Ruby on Rails" tutorial + language_name: "Python", # Buildpack name Heroku "Ruby" buildpack + buildpack_url: "https://github.com/heroku/buildpacks-python", + buildpack_name: "heroku/python", # Name of buildpack in registry + dependency_file: "requirements.txt", # File needed for detection + install_cmd_grep: "pip install", # A command (or other string) notable in the build output + fir_tutorial_url: "https://devcenter.heroku.com/articles/getting-started-with-python-fir" + ) +%> +``` + +``` +:::>> rundoc.require "../shared/intro.md" +:::>> rundoc.require "../shared/install_pack.md" +:::>> rundoc.require "../shared/configure_builder.md" +:::>> rundoc.require "../shared/what_is_a_builder.md" +:::>> rundoc.require "../shared/download.md" +:::>> rundoc.require "../shared/pack_build.md" +:::>> rundoc.require "../shared/what_is_pack_build.md" +:::>> rundoc.require "../shared/use_the_image.md" +:::>> rundoc.require "../shared/image_structure.md" +:::>> rundoc.require "../shared/call_to_action.md" +:::>> rundoc.require "../shared/procfile.md" +:::>> rundoc.require "../shared/cleanup.md" +``` diff --git a/docs/src/ruby/RUNDOC.md b/docs/src/ruby/RUNDOC.md new file mode 100644 index 0000000..840e895 --- /dev/null +++ b/docs/src/ruby/RUNDOC.md @@ -0,0 +1,33 @@ +```ruby +:::-- rundoc.require "../shared/setup.md" +:::-- print.erb +<% + setup( + app_url: "https://github.com/heroku/ruby-getting-started", + which_binary: "ruby", # Executable on disk after buildpack i.e. `which ruby` + tutorial_type: "Ruby on Rails", # Type of app "Ruby on Rails" tutorial + language_name: "Ruby", # Buildpack name Heroku "Ruby" buildpack + buildpack_url: "https://github.com/heroku/buildpacks-ruby", + buildpack_name: "heroku/ruby", # Name of buildpack in registry + dependency_file: "Gemfile", # File needed for detection + install_cmd_grep: "bundle install", # A command (or other string) notable in the build output + fir_tutorial_url: "https://devcenter.heroku.com/articles/getting-started-with-ruby-fir" + ) +%> +``` + +``` +:::>> rundoc.require "../shared/intro.md" +:::>> rundoc.require "../shared/install_pack.md" +:::>> rundoc.require "../shared/configure_builder.md" +:::>> rundoc.require "../shared/what_is_a_builder.md" +:::>> rundoc.require "../shared/download.md" +:::>> rundoc.require "../shared/pack_build.md" +:::>> rundoc.require "../shared/what_is_pack_build.md" +:::>> rundoc.require "../shared/use_the_image.md" +:::>> rundoc.require "../shared/image_structure.md" +:::>> rundoc.require "../shared/call_to_action.md" +:::>> rundoc.require "./multiple_langs.md" +:::>> rundoc.require "../shared/procfile.md" +:::>> rundoc.require "../shared/cleanup.md" +``` diff --git a/docs/src/ruby/intro.md b/docs/src/ruby/intro.md deleted file mode 100644 index 4b322c4..0000000 --- a/docs/src/ruby/intro.md +++ /dev/null @@ -1,5 +0,0 @@ -# Heroku Ruby Cloud Native Buildpack (CNB) Tutorial - -Build a Ruby on Rails application image in 5 minutes, no Dockerfile required. - -At the end of this tutorial, you'll have a working [OCI image](https://opencontainers.org/) of a Ruby on Rails application that can run locally. You will learn about the Cloud Native Buildpack (CNB) ecosystem, and how to utilize the [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/) to build images without the need to write or maintain a Dockerfile. diff --git a/docs/src/ruby/multiple_langs.md b/docs/src/ruby/multiple_langs.md index 4c1d23a..1bee0b5 100644 --- a/docs/src/ruby/multiple_langs.md +++ b/docs/src/ruby/multiple_langs.md @@ -1,6 +1,9 @@ ## Configuring multiple languages -Language support is provided by individual buildpacks that are shipped with the builder. The above example uses the `heroku/ruby` buildpack which is [visible on GitHub](https://github.com/heroku/buildpacks-ruby). When you execute `pack build` with a builder, every buildpack has the opportunity to "detect" if it should execute against that project. The `heroku/ruby` buildpack looks for a `Gemfile.lock` in the root of the project and if found, knows how to detect a Ruby version and install dependencies. +```md +:::-> print.erb +Language support is provided by individual buildpacks that are shipped with the builder. The above example uses the `<%= buildpack_name %>` buildpack which is [visible on GitHub](<%= buildpack_url %>). When you execute `pack build` with a builder, every buildpack has the opportunity to "detect" if it should execute against that project. The `<%= buildpack_name %>` buildpack looks for a <%= dependency_file %> file in the root of the project and if found, knows how to detect a <%= which_binary %> version and install dependencies. +``` In addition to this auto-detection behavior, you can specify buildpacks through the `--buildpack` flag with the `pack` CLI or through a [project.toml](https://buildpacks.io/docs/for-app-developers/how-to/build-inputs/specify-buildpacks/) file at the root of your application. @@ -33,7 +36,7 @@ Ensure that a `requirements.txt` file, a `package.json` file and a `Gemfile.lock $ pack build my-image-name --path . :::-- $ touch requirements.txt :::-- $ docker rmi -f my-image-name -:::-> $ pack build my-image-name --path . --pull-policy if-not-present --clear-cache | sed --regexp-extended -e '/Fetching gem metadata/,/Bundle complete/d' -e "/$(date --iso)/d" -e 's/Done \(.+s\)/Done/' -e 's/\.\.\.+ \(.+s\)/.../' -e 's/\b[0-9a-f]{12}\b/abcdef012345/' +:::-> $ pack build my-image-name --path . --pull-policy if-not-present --clear-cache ``` You can run the image and inspect everything is installed as expected: diff --git a/docs/src/ruby/ruby_tutorial.md b/docs/src/ruby/ruby_tutorial.md deleted file mode 100644 index d72dbed..0000000 --- a/docs/src/ruby/ruby_tutorial.md +++ /dev/null @@ -1,47 +0,0 @@ -``` -:::>> rundoc.require "./intro.md" -``` - -``` -:::>> rundoc.require "../shared/install_pack.md" -``` - -``` -:::>> rundoc.require "../shared/configure_builder.md" -``` - -``` -:::>> rundoc.require "../shared/what_is_a_builder.md" -``` - -``` -:::>> rundoc.require "./download.md" -``` - -``` -:::>> rundoc.require "../shared/pack_build.md" -``` - -``` -:::>> rundoc.require "./what_is_pack_build.md" -``` - -``` -:::>> rundoc.require "../shared/use_the_image.md" -``` - -``` -:::>> rundoc.require "./image_structure.md" -``` - -``` -:::>> rundoc.require "../shared/call_to_action.md" -``` - -``` -:::>> rundoc.require "./multiple_langs.md" -``` - -``` -:::>> rundoc.require "../shared/procfile.md" -``` diff --git a/docs/src/scala/RUNDOC.md b/docs/src/scala/RUNDOC.md new file mode 100644 index 0000000..99d22c5 --- /dev/null +++ b/docs/src/scala/RUNDOC.md @@ -0,0 +1,32 @@ +```ruby +:::-- rundoc.require "../shared/setup.md" +:::-- print.erb +<% + setup( + app_url: "https://github.com/heroku/scala-getting-started", + which_binary: "java", # Executable on disk after buildpack i.e. `which ruby` + tutorial_type: "Scala", # Type of app "Ruby on Rails" tutorial + language_name: "Java", # Buildpack name Heroku "Ruby" buildpack + buildpack_url: "https://github.com/heroku/buildpacks-jvm", + buildpack_name: "heroku/scala", # Name of buildpack in registry + dependency_file: "build.sbt", # File needed for detection + install_cmd_grep: "sbt compile", # A command (or other string) notable in the build output + fir_tutorial_url: "https://devcenter.heroku.com/articles/getting-started-with-scala-fir" + ) +%> +``` + +``` +:::>> rundoc.require "../shared/intro.md" +:::>> rundoc.require "../shared/install_pack.md" +:::>> rundoc.require "../shared/configure_builder.md" +:::>> rundoc.require "../shared/what_is_a_builder.md" +:::>> rundoc.require "../shared/download.md" +:::>> rundoc.require "../shared/pack_build.md" +:::>> rundoc.require "../shared/what_is_pack_build.md" +:::>> rundoc.require "../shared/use_the_image.md" +:::>> rundoc.require "../shared/image_structure.md" +:::>> rundoc.require "../shared/call_to_action.md" +:::>> rundoc.require "../shared/procfile.md" +:::>> rundoc.require "../shared/cleanup.md" +``` diff --git a/docs/src/shared/cleanup.md b/docs/src/shared/cleanup.md new file mode 100644 index 0000000..8879d8d --- /dev/null +++ b/docs/src/shared/cleanup.md @@ -0,0 +1,11 @@ +```ruby +:::-- pre.erb rundoc.configure +Rundoc.configure do |config| + # Use ERB defined earlier to generate a template + config.after_build do |context| + context.output_dir.join(".gitignore").write(<<~EOF) + <%= dir_name %> + EOF + end +end +``` diff --git a/docs/src/ruby/download.md b/docs/src/shared/download.md similarity index 58% rename from docs/src/ruby/download.md rename to docs/src/shared/download.md index f1907c0..59421a2 100644 --- a/docs/src/ruby/download.md +++ b/docs/src/shared/download.md @@ -1,18 +1,22 @@ -## Download an example Ruby on Rails application +```md +:::-> print.erb +## Download an example <%= tutorial_type %> application How do you configure a CNB? Give them an application. While Dockerfile is procedural, buildpacks, are declarative. A buildpack will determine what your application needs to function by inspecting the code on disk. -For this example, we're using a pre-built Ruby on Rails application. Download it now: +For this example, we're using a pre-built <%= tutorial_type %> application. Download it now: ``` -:::>- $ git clone https://github.com/heroku/ruby-getting-started -:::>- $ cd ruby-getting-started + +``` +:::>- pre.erb $ git clone <%= app_url %> +:::>- pre.erb $ cd <%= dir_name %> ``` Verify you're in the correct directory: ``` -:::>> $ ls +:::>> $ ls -A ``` This tutorial was built using the following commit SHA: diff --git a/docs/src/ruby/image_structure.md b/docs/src/shared/image_structure.md similarity index 81% rename from docs/src/ruby/image_structure.md rename to docs/src/shared/image_structure.md index 760c3db..7469cac 100644 --- a/docs/src/ruby/image_structure.md +++ b/docs/src/shared/image_structure.md @@ -5,16 +5,19 @@ If you’re an advanced `Dockerfile` user you might be interested in learning more about the internal structure of the image on disk. You can access the image disk interactively by using the `bash` docker command above. -If you view the root directory `/` you’ll see there is a `layers` folder. Every buildpack that executes gets a unique folder. For example: +If you view the root directory `/` you’ll see there is a `layers` folder. Every buildpack that executes gets a unique folder: ``` -:::>> $ docker run --rm my-image-name "ls /layers | grep ruby" +:::>> $ docker run --rm my-image-name "ls /layers" ``` -Individual buildpacks can compose multiple layers from their buildpack directory. For example you can see that `ruby` binary is present within that ruby buildpack directory: +``` +:::-> print.erb +Individual buildpacks can compose multiple layers from their buildpack directory. For example you can see that `<%= which_binary %>` binary is present within that buildpack layer directory: +``` ``` -:::>> $ docker run --rm my-image-name "which ruby" +:::>> pre.erb $ docker run --rm my-image-name "which <%= which_binary %>" ``` OCI images are represented as sequential modifications to disk. By scoping buildpack disk modifications to their own directory, the CNB API guarantees that changes to a layer in one buildpack will not affect the contents of disk to another layer. This means that OCI images produced by CNBs are rebaseable by default, while those produced by Dockerfile are not. diff --git a/docs/src/shared/intro.md b/docs/src/shared/intro.md new file mode 100644 index 0000000..b73c24a --- /dev/null +++ b/docs/src/shared/intro.md @@ -0,0 +1,12 @@ +```md +:::-> print.erb +# Heroku <%= language_name %> Cloud Native Buildpack (CNB) Tutorial + +Build a <%= tutorial_type %> application image in 5 minutes, no Dockerfile required. + +At the end of this tutorial, you'll have a working [OCI image](https://opencontainers.org/) of a <%= tutorial_type %> application that can run locally. You will learn about the Cloud Native Buildpack (CNB) ecosystem, and how to utilize the [pack CLI](https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/) to build images without the need to write or maintain a Dockerfile. + +> You can now also use the `<%= buildpack_name %>` CNB on Heroku [Fir generation](https://devcenter.heroku.com/articles/generations#fir) via the [pilot program](https://hello.heroku.com/next-generation-sign-up). See the [Getting started on Heroku Fir Dev Center tutorial](<%= fir_tutorial_url %>). + + +``` diff --git a/docs/src/shared/pack_build.md b/docs/src/shared/pack_build.md index df21846..32caff0 100644 --- a/docs/src/shared/pack_build.md +++ b/docs/src/shared/pack_build.md @@ -4,10 +4,20 @@ Now build an image named `my-image-name` by executing the heroku builder against `pack build` command: ``` -$ pack build my-image-name --path . :::-- $ docker rmi -f my-image-name :::-- $ pack build my-image-name --path . --pull-policy if-not-present --clear-cache 2>&1 | tee build_output.txt -:::-> $ cat build_output.txt | sed --regexp-extended -e '/Fetching gem metadata/,/Bundle complete/d' -e "/$(date --iso)/d" -e 's/Done \(.+s\)/Done/' -e 's/\.\.\.+ \(.+s\)/.../' -e 's/\b[0-9a-f]{12}\b/abcdef012345/' +:::>> print.text $ pack build my-image-name --path . +:::>> print.erb +<%= +contents = File.read("build_output.txt").lines +if pack_build_head_tail + (contents.first(pack_build_head_tail) + + ["...\n"] + + contents.last(pack_build_head_tail) + ) +else + contents +end.join("") %> ``` > [!NOTE] @@ -16,6 +26,5 @@ $ pack build my-image-name --path . Verify that you see “Successfully built image my-image-name” at the end of the output. And verify that the image is present locally: ``` -$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" | grep my-image-name -:::-> $ docker image ls --format "table abcdef012345\t{{.Repository}}\t{{.Tag}}" | grep my-image-name +:::>> $ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" | grep my-image-name ``` diff --git a/docs/src/shared/procfile.md b/docs/src/shared/procfile.md index 9f1950a..90d39b0 100644 --- a/docs/src/shared/procfile.md +++ b/docs/src/shared/procfile.md @@ -4,10 +4,10 @@ Most buildpacks rely on existing community standards to allow you to configure y The `Procfile` is a configuration file format that was [introduced by Heroku in 2011](https://devcenter.heroku.com/articles/procfile), you can now use this behavior on your CNB-powered application via the `heroku/procfile`, which like the rest of the buildpacks in our builder [is open source](https://github.com/heroku/buildpacks-procfile). The `heroku/procfile` buildpack allows you to configure your web startup process. -This is the `Procfile` of the getting started guide: +This is the `web` entry in the getting started guide's `Procfile`: ``` -:::-> $ cat Procfile +:::-> $ cat Procfile | grep "^web" ``` By including this file and using `heroku/procfile` buildpack, your application will receive a default web process. You can configure this behavior by changing the contents of that file. diff --git a/docs/src/shared/setup.md b/docs/src/shared/setup.md new file mode 100644 index 0000000..2650c97 --- /dev/null +++ b/docs/src/shared/setup.md @@ -0,0 +1,27 @@ +```ruby +:::-- print.erb +<% +require 'uri' + +class << self + attr_reader :app_url, :buildpack_url, :dir_name, :tutorial_type, :buildpack_name, :dependency_file, :language_name, :install_cmd_grep, :which_binary, :fir_tutorial_url, :pack_build_head_tail +end + +def setup(app_url: , buildpack_url:, tutorial_type: , buildpack_name: , dependency_file: , language_name: , install_cmd_grep:, which_binary: , fir_tutorial_url:, pack_build_head_tail: false) + @app_url = app_url + @dir_name = File.basename(URI.parse(@app_url).path) + @which_binary = which_binary + @buildpack_url = buildpack_url + @language_name = language_name + @tutorial_type = tutorial_type + @buildpack_name = buildpack_name + @dependency_file = dependency_file + @install_cmd_grep = install_cmd_grep + @fir_tutorial_url = fir_tutorial_url + # Falsey or an integer for number of lines to show of the head and Tail of the `pack build` command + # When `false` it will emit the entire command + @pack_build_head_tail = pack_build_head_tail +end + +%> +``` diff --git a/docs/src/shared/use_the_image.md b/docs/src/shared/use_the_image.md index a2202e3..edb21f7 100644 --- a/docs/src/shared/use_the_image.md +++ b/docs/src/shared/use_the_image.md @@ -5,16 +5,16 @@ Even though we used `pack` and CNBs to build our image, it can be run with your By default, images will be booted into a web server configuration. You can launch the app we just built by running: ``` -$ docker run -it --rm --env PORT=9292 -p 9292:9292 my-image-name -:::-> background.start("docker run --rm --env PORT=9292 -p 9292:9292 my-image-name", name: "docker_server") +$ docker run -it --rm --env PORT=5006 -p 5006:5006 my-image-name +:::-> background.start("docker run --rm --env PORT=5006 -p 5006:5006 my-image-name", name: "docker_server") :::-- $ sleep 10 :::-> background.log.read(name: "docker_server") ``` -Now when you visit [http://localhost:9292](http://localhost:9292) you should see a working web application: +Now when you visit [http://localhost:5006](http://localhost:5006) you should see a working web application: ``` -:::>> website.visit(name: "localhost", url: "http://localhost:9292") +:::>> website.visit(name: "localhost", url: "http://localhost:5006") :::>> website.screenshot(name: "localhost") ``` @@ -30,8 +30,8 @@ Here's a quick breakdown of that command we just ran: - `docker run` Create and run a new container from an image. - `-it` Makes the container interactive and allocates a TTY. - `--rm` Automatically remove the container when it exits. -- `--env PORT=9292` Creates an environment variable named `PORT` and sets it to `9292` this is needed so the application inside the container knows what port to bind the web server. -- `-p 9292:9292` Publishes a container's port(s) to the host. This is what allows requests from your machine to be received by the container. +- `--env PORT=5006` Creates an environment variable named `PORT` and sets it to `5006` this is needed so the application inside the container knows what port to bind the web server. +- `-p 5006:5006` Publishes a container's port(s) to the host. This is what allows requests from your machine to be received by the container. - `my-image-name` The name of the image you want to use for the application. So far, we've downloaded an application via git and run a single command `pack build` to generate an image, and then we can use that image as if it was generated via a Dockerfile via the `docker run` command. @@ -45,8 +45,8 @@ $ docker run -it --rm my-image-name bash Now you can inspect the container interactively. For example, you can see the files on disk with `ls`: ``` -$ ls -:::-> $ docker run --rm my-image-name ls +:::>> print.text $ ls -A +:::-> $ docker run --rm my-image-name "ls -A" ``` And anything else you would typically do via an interactive container session. diff --git a/docs/src/ruby/what_is_pack_build.md b/docs/src/shared/what_is_pack_build.md similarity index 71% rename from docs/src/ruby/what_is_pack_build.md rename to docs/src/shared/what_is_pack_build.md index 5ec4008..c025eb0 100644 --- a/docs/src/ruby/what_is_pack_build.md +++ b/docs/src/shared/what_is_pack_build.md @@ -3,18 +3,23 @@ > [!NOTE] > Skip ahead if you want to run the application first and get into the details later. -When you run `pack build` with a builder, each buildpack runs a detection script to determine if it should be eligible to build the application. In our case the `heroku/ruby` buildpack found a `Gemfile.lock` file and `heroku/nodejs-engine` buildpack found a `package.json` file on disk. As a result, both buildpacks have enough information to install Ruby and Node dependencies. You can view a list of the buildpacks used in the output above: +``` +:::-> print.erb +When you run `pack build` with a builder, each buildpack runs a detection script to determine if it should be eligible to build the application. In our case the `<%= buildpack_name %>` buildpack found a `<%= dependency_file %>` file. As a result, the buildpack has enough information to install <%= language_name %> dependencies. You can view a list of the buildpacks used in the build output: +``` ``` :::-> $ grep DETECTING -A5 ./build_output.txt ``` -After the detect phase, each buildpack will execute. Buildpacks can inspect your project, install files to disk, run commands, write environment variables, [and more](https://buildpacks.io/docs/for-buildpack-authors/). You can see some examples of that in the output above. For example, the Ruby buildpack installs dependencies from the `Gemfile` automatically: - ``` -:::-> $ grep "bundle install" -m1 ./build_output.txt +:::-> print.erb +After the detect phase, each buildpack will execute. Buildpacks can inspect your project, install files to disk, run commands, write environment variables, [and more](https://buildpacks.io/docs/for-buildpack-authors/). You can see some examples of that in the output above. For example, the <%= language_name %> buildpack installs dependencies from the `<%= dependency_file %>` automatically: ``` -If you’re familiar with Dockerfile you might know that [many commands in a Dockerfile will create a layer](https://dockerlabs.collabnix.com/beginners/dockerfile/Layering-Dockerfile.html). Buildpacks also use layers, but the CNB buildpack API provides for fine grained control over what exactly is in these layers and how they’re composed. Unlike Dockerfile, all images produced by CNBs [can be rebased](https://tag-env-sustainability.cncf.io/blog/2023-12-reduce-reuse-rebase-buildpacks/#reduce-reuserebase). The CNB api also improves on many of the pitfalls outlined in the satirical article [Write a Good Dockerfile in 19 'Easy' Steps](https://jkutner.github.io/2021/04/26/write-good-dockerfile.html). +``` +:::-> pre.erb $ grep "<%= install_cmd_grep %>" -m1 ./build_output.txt +``` +If you’re familiar with Dockerfile you might know that [many commands in a Dockerfile will create a layer](https://dockerlabs.collabnix.com/beginners/dockerfile/Layering-Dockerfile.html). Buildpacks also use layers, but the CNB buildpack API provides for fine grained control over what exactly is in these layers and how they’re composed. Unlike Dockerfile, all images produced by CNBs [can be rebased](https://tag-env-sustainability.cncf.io/blog/2023-12-reduce-reuse-rebase-buildpacks/#reduce-reuserebase). The CNB api also improves on many of the pitfalls outlined in the satirical article [Write a Good Dockerfile in 19 'Easy' Steps](https://jkutner.github.io/2021/04/26/write-good-dockerfile.html).