Skip to content

Commit 59886fe

Browse files
Improve launch process parsing (#124)
1 parent 532d09c commit 59886fe

File tree

11 files changed

+246
-75
lines changed

11 files changed

+246
-75
lines changed

.github/workflows/ci.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ jobs:
3333
args: "--diff ."
3434
- name: Run Rubocop
3535
run: bundle exec rubocop
36+
- name: Install the latest version of uv
37+
uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
38+
with:
39+
enable-cache: false
40+
- name: Run Ruff check
41+
run: uvx ruff check --output-format=github
42+
- name: Run Ruff format
43+
run: uvx ruff format --diff
3644

3745
integration-test:
3846
runs-on: ubuntu-24.04
@@ -68,5 +76,7 @@ jobs:
6876
steps:
6977
- name: Checkout
7078
uses: actions/checkout@v5
79+
- name: Run Python unit tests
80+
run: make run-python-tests
7181
- name: Run buildpack using default app fixture
7282
run: make run

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@
55
vendor/bundle/
66
.DS_Store
77
.rspec_status
8+
9+
# Python-generated files
10+
__pycache__/
11+
*.py[oc]

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## [Unreleased]
44

5+
### Changed
6+
7+
- Improved launch process detection with robust `launch.toml` parsing. ([#124](https://github.com/heroku/heroku-buildpack-dotnet/pull/124))
58

69
## [v32] - 2025-09-12
710

Makefile

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,43 @@
11
# These targets are not files
2-
.PHONY: lint lint-scripts lint-ruby check-format format run run-ci publish
2+
.PHONY: lint lint-scripts lint-ruby lint-python check-format check-format-shell check-format-python format format-shell format-python run-python-tests run run-ci publish
33

44
STACK ?= heroku-24
55
FIXTURE ?= spec/fixtures/basic_web_8.0
66

77
# Converts a stack name of `heroku-NN` to its build Docker image tag of `heroku/heroku:NN-build`.
88
STACK_IMAGE_TAG := heroku/$(subst -,:,$(STACK))-build
99

10-
lint: lint-scripts check-format lint-ruby
10+
lint: lint-scripts check-format lint-ruby lint-python
1111

1212
lint-scripts:
1313
@git ls-files -z --cached --others --exclude-standard 'bin/*' '*/bin/*' '*.sh' | xargs -0 shellcheck --check-sourced --color=always
1414

1515
lint-ruby:
1616
@bundle exec rubocop
1717

18-
check-format:
18+
lint-python:
19+
@ruff check .
20+
21+
check-format: check-format-shell check-format-python
22+
23+
check-format-shell:
1924
@shfmt --diff .
2025

21-
format:
26+
check-format-python:
27+
@ruff format --diff
28+
29+
format: format-shell format-python
30+
31+
format-shell:
2232
@shfmt --write --list .
2333

34+
format-python:
35+
@ruff format .
36+
37+
run-python-tests:
38+
@echo "Running Python unit tests using: STACK=$(STACK)"
39+
@docker run --rm -v $(PWD):/src:ro "$(STACK_IMAGE_TAG)" python3 -m unittest discover -s /src/tests -p "test_*.py" -v
40+
2441
run:
2542
@echo "Running buildpack using: STACK=$(STACK) FIXTURE=$(FIXTURE)"
2643
@docker run --rm -v $(PWD):/src:ro --tmpfs /app -e "HOME=/app" -e "STACK=$(STACK)" "$(STACK_IMAGE_TAG)" \

bin/release

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,5 @@ launch_toml_file="${CNB_BUILD_DIR}/layers/launch.toml"
1111
# Check if launch.toml exists and output default_process_types in YAML
1212
if [[ -f "${launch_toml_file}" ]]; then
1313
BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd)
14-
source "${BUILDPACK_DIR:?}/lib/cnb_process_utils.sh"
15-
16-
declare -A launch_processes
17-
parse_launch_processes launch_processes "${launch_toml_file}"
18-
19-
echo "---"
20-
echo "default_process_types:"
21-
for type in "${!launch_processes[@]}"; do
22-
echo " ${type}: ${launch_processes[${type}]}"
23-
done
14+
python3 "${BUILDPACK_DIR}/lib/parse_launch_toml.py" "${launch_toml_file}" --yaml
2415
fi

bin/test

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,15 @@ CNB_BUILD_DIR="${BUILD_DIR}/.heroku/cnb/dotnet"
99

1010
BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd)
1111

12-
source "${BUILDPACK_DIR:?}/lib/cnb_process_utils.sh"
1312
source "${BUILDPACK_DIR:?}/lib/output.sh"
1413

15-
declare -A launch_processes
16-
parse_launch_processes launch_processes "${CNB_BUILD_DIR}/layers/launch.toml"
17-
1814
test_process_type="test"
19-
if [[ -n "${launch_processes[${test_process_type}]:-}" ]]; then
20-
echo "Running ${test_process_type} command: \`${launch_processes[${test_process_type}]}\`" | output::indent
15+
16+
if test_command=$(python3 "${BUILDPACK_DIR}/lib/parse_launch_toml.py" "${CNB_BUILD_DIR}/layers/launch.toml" --process "${test_process_type}"); then
17+
echo "Running ${test_process_type} command: \`${test_command}\`" | output::indent
2118

2219
cd "${BUILD_DIR}"
23-
eval "${launch_processes[${test_process_type}]}" |& output::indent
20+
eval "${test_command}" |& output::indent
2421
else
2522
# A `test` process type should always be added during compilation, so
2623
# (official GitHub/buildpack registry releases) should never produce

buildpack.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ files = [
77
"etc/publish.sh",
88
"lib/validate_report.sh",
99
"spec/",
10+
"tests/",
1011
".editorconfig",
1112
".gitignore",
1213
".rubocop.yml",

lib/cnb_process_utils.sh

Lines changed: 0 additions & 53 deletions
This file was deleted.

lib/parse_launch_toml.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Parse CNB launch.toml files for buildpack scripts.
4+
5+
Supports two output formats:
6+
- --yaml: YAML for bin/release
7+
- --process <type>: Command for a single process type (e.g., as used in `bin/test`)
8+
"""
9+
10+
import sys
11+
import shlex
12+
from pathlib import Path
13+
14+
# `tomli`/`tomllib` compatibility layer: Use `tomllib` if available in the
15+
# standard library (Python 3.11+), and fallback to `tomli` (available in
16+
# `heroku/heroku:22-build`) when the standard library module is not available.
17+
# See https://github.com/hukkin/tomli#building-a-tomlitomllib-compatibility-layer
18+
if sys.version_info >= (3, 11):
19+
import tomllib
20+
else:
21+
import tomli as tomllib
22+
23+
24+
def parse_processes(toml_path):
25+
"""Parse launch.toml and return process type -> command mapping."""
26+
try:
27+
with open(toml_path, "rb") as f:
28+
data = tomllib.load(f)
29+
except (FileNotFoundError, tomllib.TOMLDecodeError):
30+
return {}
31+
32+
processes = {}
33+
for proc in data.get("processes", []):
34+
proc_type = proc.get("type")
35+
command_list = proc.get("command")
36+
37+
if not proc_type or not isinstance(command_list, list):
38+
continue
39+
40+
# Extract script content from bash -c commands, otherwise join with escaping
41+
if len(command_list) >= 3 and command_list[:2] == ["bash", "-c"]:
42+
processes[proc_type] = command_list[2]
43+
else:
44+
processes[proc_type] = shlex.join(command_list)
45+
46+
return processes
47+
48+
49+
def main():
50+
if len(sys.argv) not in [3, 4]:
51+
print(
52+
"Usage: parse_launch_toml.py <launch.toml> [--yaml|--process <type>]",
53+
file=sys.stderr,
54+
)
55+
sys.exit(1)
56+
57+
toml_path, mode = sys.argv[1], sys.argv[2]
58+
59+
if not Path(toml_path).exists():
60+
sys.exit(1)
61+
62+
processes = parse_processes(toml_path)
63+
64+
if mode == "--yaml":
65+
if processes:
66+
print("---\ndefault_process_types:")
67+
for proc_type, command in processes.items():
68+
print(f" {proc_type}: {command}")
69+
70+
elif mode == "--process" and len(sys.argv) == 4:
71+
command = processes.get(sys.argv[3])
72+
if command:
73+
print(command)
74+
else:
75+
sys.exit(1)
76+
77+
else:
78+
print(
79+
"Usage: parse_launch_toml.py <launch.toml> [--yaml|--process <type>]",
80+
file=sys.stderr,
81+
)
82+
sys.exit(1)
83+
84+
85+
if __name__ == "__main__":
86+
main()

spec/hatchet/process_type_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
it 'runs the expected test command in CI' do
1515
app.run_ci do |test_run|
1616
expect(clean_output(test_run.output)).to include(<<~OUTPUT)
17-
Running test command: `dotnet test "solution with spaces.sln" --verbosity normal`
17+
Running test command: `dotnet test 'solution with spaces.sln' --verbosity normal`
1818
OUTPUT
1919
end
2020
end

0 commit comments

Comments
 (0)