diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6d150096..3bfa50f9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,4 +1,5 @@ version: 2 + updates: - package-ecosystem: "github-actions" directory: "/" @@ -9,10 +10,7 @@ updates: update-types: - "minor" - "patch" - - package-ecosystem: "gomod" - directories: - - "/*" - - "/*/*" + directory: "/" schedule: interval: "daily" diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml new file mode 100644 index 00000000..406f57fd --- /dev/null +++ b/.github/workflows/pre-commit.yaml @@ -0,0 +1,42 @@ +name: pre-commit + +on: + push: + branches: + - "main" # required to create a usable cache for other PRs + pull_request: + branches: + - "main" + +permissions: {} + +jobs: + lint: + name: pre-commit checks + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Harden Runner + uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2.19.3 + with: + egress-policy: audit + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 # required for --from-ref and --to-ref + persist-credentials: false + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + - run: | + python -m pip install pre-commit + python -m pip freeze --local + - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + id: cache + with: + path: ~/.cache/pre-commit + key: ${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }} + # A cache miss means that the pre-commit config changed and we should check everything + # Otherwise, we can just check modified files which will be much faster + - if: steps.cache.outputs.cache-hit != 'true' + run: pre-commit run --show-diff-on-failure --color=always --all-files + - if: steps.cache.outputs.cache-hit == 'true' + run: pre-commit run --show-diff-on-failure --color=always --from-ref HEAD^^^ --to-ref HEAD diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml new file mode 100644 index 00000000..d819f449 --- /dev/null +++ b/.markdownlint-cli2.yaml @@ -0,0 +1,7 @@ +config: + MD013: + line_length: 120 + MD046: + style: fenced + MD060: + style: compact diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..df2e9367 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,34 @@ +# Pre-commit configuration for tw repository +# Install: pip install pre-commit +# Setup: pre-commit install +# Run manually: pre-commit run --all-files +repos: + - repo: https://github.com/chainguard-dev/yam + rev: d05ffb50d1a8f8e7047a77e57280147ff19809d4 # frozen: v0.2.54 + hooks: + - id: yam + files: '\.ya?ml$' + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 + hooks: + - id: check-yaml + files: '\.ya?ml$' + - id: check-merge-conflict + - id: check-added-large-files + - id: check-case-conflict + - id: detect-private-key + - id: end-of-file-fixer + - id: trailing-whitespace + # Ensure markdown files pass stereo's markdownlint checks when synced + - repo: https://github.com/DavidAnson/markdownlint-cli2 + rev: 3766ad839ad3a74558031510b3bc9872bb1d9980 # frozen: v0.22.0 + hooks: + - id: markdownlint-cli2 + files: '\.md$' + - repo: local + hooks: + - id: shellcheck + name: shellcheck + entry: make shellcheck + language: system + pass_filenames: false diff --git a/README.md b/README.md index 65fafe76..9e6b12c4 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,17 @@ # tw (tee-dub) + tw (pronounced tee-dub) is a centralized repository for testing and building tools or helpers. ## Release to stereo + To release a version of tw to stereo, run tools/release-to-stereo. - $ git tag vX.Y.Z - $ git push origin vX.Y.Z - $ ./tools/release-to-stereo vX.Y.Z ~/git/cg/chainguard-dev/stereo/ +```bash +git tag vX.Y.Z +git push origin vX.Y.Z +./tools/release-to-stereo vX.Y.Z ~/git/cg/chainguard-dev/stereo/ +``` This takes care of updating the `tw.yaml` file from `melange.yaml`, and syncs the pipeline files for other dirs. @@ -19,6 +23,7 @@ That will do a commit and you just need to push and do a PR. This repository contains three types of tests to ensure quality and correctness: ### 1. Main Package Tests (`test-melange`) + Tests the main `tw` package defined in `melange.yaml`. ```bash @@ -28,6 +33,7 @@ make test-melange This validates that the tw tools package builds and functions correctly. ### 2. Project Tests (`test-projects`) + Tests individual project subdirectories (e.g., `ldd-check`, `package-type-check`, `gosh`, etc.). ```bash @@ -41,6 +47,7 @@ make test-project/package-type-check Each project directory contains its own test suite specific to that tool. ### 3. Pipeline Validation Tests (`test-pipelines`) + Tests the pipeline validators located in `pipelines/test/tw/` using test packages in `tests/`. ```bash @@ -52,17 +59,20 @@ This runs a complete test suite that: 4. Runs pipeline validation tests against those packages **Test files structure:** + - `tests/docs-test.yaml` - Tests the `pipelines/test/tw/docs.yaml` pipeline - `tests/staticpackage-test.yaml` - Tests the `pipelines/test/tw/staticpackage.yaml` pipeline - `tests/emptypackage-test.yaml` - Tests the `pipelines/test/tw/emptypackage.yaml` pipeline Each test file contains: + - **Positive tests**: Valid packages that should pass the pipeline check - **Negative tests**: Invalid packages that should be rejected by the pipeline More about the pipeline tests can be found in the [tests/README.md](tests/README.md) file. ### 4. Run All Tests + Run the complete test suite (all three test types): ```bash @@ -72,6 +82,7 @@ make test-all This executes all tests in sequence: `test-melange` → `test-projects` → `test-pipelines` ### 5. Individual Test Targets + For more granular control: ```bash @@ -83,16 +94,16 @@ make run-pipeline-tests # Only run pipeline tests (assumes already built) To test a tw pipeline in a local stereo repository, you need the following steps: -* Build the tw tools package in this repository, `make build`. -* If required, sync the pipeline yaml to stereo by hand. -* If required, build the melange package with the new pipeline, in the stereo repository. -* If required, test the melange package with the new pipeline, in the stereo repository. +- Build the tw tools package in this repository, `make build`. +- If required, sync the pipeline yaml to stereo by hand. +- If required, build the melange package with the new pipeline, in the stereo repository. +- If required, test the melange package with the new pipeline, in the stereo repository. Most likely, you need to tell the melange build in the stereo repository to use the tw index. A complete example: -``` +```console user@debian:~git/tw $ make build user@debian:~git/tw $ cp pipelines/test/tw/something.yaml ~/git/stereo/os/pipelines/test/tw/ user@debian:~git/tw $ cd ~/git/stereo/enterprise-packages/ diff --git a/header-check/README.md b/header-check/README.md index d2762517..e2ec8fbd 100644 --- a/header-check/README.md +++ b/header-check/README.md @@ -2,4 +2,5 @@ This is a script that checks the usability of .h and .hpp header files, as installed. -It will generate a basic ```configure.ac``` script, run ```autoconf``` and ```./configure```, and check the header file installed is usable in an ```autoconf``` context. +It will generate a basic ```configure.ac``` script, run ```autoconf``` and ```./configure```, +and check the header file installed is usable in an ```autoconf``` context. diff --git a/no-docs-check/Makefile b/no-docs-check/Makefile index 82928017..22ccfe1f 100644 --- a/no-docs-check/Makefile +++ b/no-docs-check/Makefile @@ -15,4 +15,4 @@ clean: melange-install: build mkdir -p $(MELANGE_INSTALL_PATH) - install -Dm755 no-docs-check $(MELANGE_INSTALL_PATH)/no-docs-check \ No newline at end of file + install -Dm755 no-docs-check $(MELANGE_INSTALL_PATH)/no-docs-check diff --git a/package-type-check/Makefile b/package-type-check/Makefile index 40d7e815..10d377fc 100644 --- a/package-type-check/Makefile +++ b/package-type-check/Makefile @@ -15,4 +15,4 @@ clean: melange-install: build mkdir -p $(MELANGE_INSTALL_PATH) - install -Dm755 package-type-check $(MELANGE_INSTALL_PATH)/package-type-check \ No newline at end of file + install -Dm755 package-type-check $(MELANGE_INSTALL_PATH)/package-type-check diff --git a/pipelines/test/tw/INDEX.md b/pipelines/test/tw/INDEX.md index 47e38d53..dd84dca0 100644 --- a/pipelines/test/tw/INDEX.md +++ b/pipelines/test/tw/INDEX.md @@ -1,11 +1,12 @@ # Test Pipelines Index -This directory contains test pipelines for validating Wolfi packages. Use this index to find the appropriate pipeline for your testing needs. +This directory contains test pipelines for validating Wolfi packages. Use this index to find the appropriate +pipeline for your testing needs. ## Quick Reference | Pipeline | Purpose | Required Inputs | -|----------|---------|-----------------| +| -------- | ------- | --------------- | | `configpackage` | Validate configuration packages contain config files | none | | `contains-files` | Verify package contains expected files | `files` or `dir`+`name` | | `debugpackage` | Validate debug symbol packages | none | @@ -38,26 +39,31 @@ This directory contains test pipelines for validating Wolfi packages. Use this i Use these pipelines to validate that packages conform to expected structural patterns for their type. ### `configpackage` + Validates configuration packages contain non-empty text config files in the expected directory. **When to use:** For `-config` packages that install runtime configuration files. **Inputs:** + - `dir` (optional, default: `/etc/`) - Directory to search for config files --- ### `docs` + Validates documentation packages contain only documentation files under a specified path prefix. **When to use:** For `-doc` packages that should only contain documentation. **Inputs:** + - `path-prefix` (optional, default: `usr/share`) - Expected path prefix for docs --- ### `devpackage` + Validates development packages contain headers, static libraries, and development files. **When to use:** For `-dev` or `-devel` packages. @@ -67,17 +73,20 @@ Validates development packages contain headers, static libraries, and developmen --- ### `srcpackage` + Validates source packages contain actual source code files in the expected directory. **When to use:** For `-src` packages that install source code. **Inputs:** + - `dir` (optional, default: `/usr/src/`) - Directory to search for source files - `name` (optional, default: `*`) - File name pattern (find syntax) --- ### `staticpackage` + Validates packages contain only static libraries (`.a` files). **When to use:** For `-static` packages. @@ -87,6 +96,7 @@ Validates packages contain only static libraries (`.a` files). --- ### `debugpackage` + Validates debug symbol packages contain appropriate debug information. **When to use:** For `-dbg` or `-debug` packages. @@ -96,6 +106,7 @@ Validates debug symbol packages contain appropriate debug information. --- ### `emptypackage` + Validates packages are empty (minimal package definition). **When to use:** For packages that intentionally contain no files. @@ -105,6 +116,7 @@ Validates packages are empty (minimal package definition). --- ### `metapackage` + Validates meta-packages that contain only dependencies, no files. **When to use:** For packages that exist solely to group dependencies. @@ -114,6 +126,7 @@ Validates meta-packages that contain only dependencies, no files. --- ### `byproductpackage` + Validates by-product packages created during build. **When to use:** For automatically generated split/companion packages. @@ -123,11 +136,13 @@ Validates by-product packages created during build. --- ### `virtualpackage` + Validates packages provide specified virtual capabilities. **When to use:** For packages that provide virtual package names (e.g., `provides: mail-transport-agent`). **Inputs:** + - `virtual-pkg-name` (required) - Space-separated list of virtual package names to verify **Dependencies:** package-type-check, busybox @@ -139,11 +154,13 @@ Validates packages provide specified virtual capabilities. Use these pipelines to verify that binaries work correctly. ### `help-check` + Verifies binaries respond correctly to help flags (`--help`, `-h`, etc.). **When to use:** To ensure CLI tools have working help output. **Inputs:** + - `bins` (required) - Space-separated list of binaries to test - `help-flag` (optional, default: `auto`) - Specific flag to test - `expect-contains` (optional) - String that must appear in help output @@ -154,11 +171,13 @@ Verifies binaries respond correctly to help flags (`--help`, `-h`, etc.). --- ### `ver-check` + Verifies binaries report the correct version information. **When to use:** To ensure CLI tools report the expected version. **Inputs:** + - `bins` (required) - Space-separated list of binaries to test - `version` (optional, default: `${{package.version}}`) - Expected version string - `version-flag` (optional, default: `auto`) - Specific flag to test @@ -170,11 +189,13 @@ Verifies binaries report the correct version information. --- ### `gem-check` + Validates Ruby gems can be properly required and loaded. **When to use:** For Ruby gem packages to verify they install and load correctly. **Inputs:** + - `package` (optional, default: `${{context.name}}`) - Package name - `require` (optional) - Gem names to test (auto-detected if not specified) @@ -189,11 +210,13 @@ Validates Ruby gems can be properly required and loaded. Use these pipelines to check for dependency issues. ### `ldd-check` + Checks binaries for missing runtime library dependencies using `ldd`. **When to use:** To verify all shared library dependencies are resolvable. **Inputs:** + - `files` (optional) - Specific files to check - `exclude-files` (optional) - Files to skip - `packages` (optional, default: `${{context.name}}`) - Packages to check @@ -205,11 +228,13 @@ Checks binaries for missing runtime library dependencies using `ldd`. --- ### `pip-check` + Validates Python package dependencies using `pip check`. **When to use:** For Python packages to verify no dependency version conflicts. **Inputs:** + - `python` (optional, default: `DEFAULT`) - Python interpreter (auto-detected) **Dependencies:** tw-pip-check @@ -217,11 +242,13 @@ Validates Python package dependencies using `pip check`. --- ### `shell-deps.check` + Checks shell script files for missing command dependencies and GNU-specific flags incompatible with busybox. **When to use:** When you have shell scripts and want to verify all commands they use are available. **Inputs:** + - `files` (required) - Shell script files to check (supports glob patterns) - `path` (optional, default: `/usr/bin`) - PATH to use for command lookup - `strict` (optional, default: `true`) - Fail on any missing dependency @@ -234,11 +261,13 @@ Checks shell script files for missing command dependencies and GNU-specific flag --- ### `shell-deps-check-packages` + Checks installed package shell scripts for dependency issues. **When to use:** To verify shell scripts in an installed package have all required commands available. **Inputs:** + - `package` (required) - Package name to check - `path` (optional, default: `/usr/bin`) - PATH to use for command lookup - `strict` (optional, default: `true`) - Fail on any missing dependency @@ -255,11 +284,13 @@ Checks installed package shell scripts for dependency issues. Use these pipelines to verify package contents. ### `header-check` + Verifies C/C++ header files compile successfully. **When to use:** For packages with header files to ensure they have valid syntax and includes. **Inputs:** + - `packages` (optional, default: `${{context.name}}`) - Packages to check - `files` (optional) - Specific header files to test - `configure-opts` (optional) - Additional compiler flags @@ -269,11 +300,13 @@ Verifies C/C++ header files compile successfully. --- ### `symlink-check` + Verifies symlinks point to valid targets and checks for absolute symlinks. **When to use:** To ensure no broken or problematic symlinks in packages. **Inputs:** + - `packages` (optional, default: `${{context.name}}`) - Packages to check - `allow-absolute` (optional, default: `false`) - Allow absolute symlinks @@ -282,26 +315,31 @@ Verifies symlinks point to valid targets and checks for absolute symlinks. --- ### `contains-files` + Verifies a package contains expected files. **When to use:** To assert specific files exist in a package. **Inputs (mode 1 - directory search):** + - `dir` (optional, default: `/usr/`) - Directory to search - `name` (optional, default: `*`) - File name pattern (find syntax) - `type` (optional, default: `f`) - File type (`f` for file, `d` for directory, etc.) **Inputs (mode 2 - direct file check):** + - `files` (optional) - Space-separated list of file paths to verify exist --- ### `no-docs` + Ensures a package contains no documentation files. **When to use:** For runtime-only or specialized packages that should not include docs. **Inputs:** + - `package` (optional, default: `${{context.name}}`) - Package to check **Dependencies:** no-docs-check @@ -309,11 +347,13 @@ Ensures a package contains no documentation files. --- ### `verify-service` + Validates systemd service/unit files for proper formatting and best practices. **When to use:** For packages that install systemd services. **Inputs:** + - `skip-files` (optional) - Space-separated files to exclude from validation - `man` (optional, default: `false`) - Include documentation tests @@ -324,6 +364,7 @@ Validates systemd service/unit files for proper formatting and best practices. ## Usage Examples ### Basic package type validation + ```yaml test: pipeline: @@ -331,6 +372,7 @@ test: ``` ### Binary version check + ```yaml test: pipeline: @@ -340,6 +382,7 @@ test: ``` ### Check for required files + ```yaml test: pipeline: @@ -349,6 +392,7 @@ test: ``` ### Shell script dependency check + ```yaml test: pipeline: diff --git a/pipelines/test/tw/pip-check.yaml.wtfisthis b/pipelines/test/tw/pip-check.yaml.wtfisthis new file mode 100644 index 00000000..c6fcec01 --- /dev/null +++ b/pipelines/test/tw/pip-check.yaml.wtfisthis @@ -0,0 +1,37 @@ +name: pip-check + +needs: + packages: + - tw-pip-check + +inputs: + files: + description: | + The files to run `ldd` on and check for missing deps. + required: false + packages: + description: | + Check all binaries in these installed packages. + Use "none" to disable the default package check. + required: false + default: "${{context.name}}" + extra-library-paths: + description: | + Prepend the provided string to the LD_LIBRARY_PATH. + For more than one path, use a ":" delimited string. + required: false + default: "" + verbose: + description: | + Should the full ldd output be shown + required: false + default: false + +pipeline: + - name: "check for missing library dependencies using ldd" + runs: | + ldd-check \ + --files="${{inputs.files}}" \ + --packages="${{inputs.packages}}" \ + --extra-library-paths="${{inputs.extra-library-paths}}" \ + --verbose="${{inputs.verbose}}" diff --git a/pipelines/test/tw/pip-check.yaml~ b/pipelines/test/tw/pip-check.yaml~ new file mode 100644 index 00000000..9e7f1e61 --- /dev/null +++ b/pipelines/test/tw/pip-check.yaml~ @@ -0,0 +1,22 @@ +name: Run 'pip check' to verify runtime deps. + +needs: + packages: + - tw-pip-check + +inputs: + python: + description: path to python interpreter to use + default: DEFAULT + +pipeline: + - name: "pip check" + runs: | + set +x + python="${{inputs.python}}" + + if [ "$python" = "DEFAULT" ]; then + tw-pip-check + else + tw-pip-check --python="$python" + fi diff --git a/symlink-check/Makefile b/symlink-check/Makefile index 8c6c1390..5c60fd25 100644 --- a/symlink-check/Makefile +++ b/symlink-check/Makefile @@ -15,4 +15,4 @@ clean: melange-install: build mkdir -p $(MELANGE_INSTALL_PATH) - install -Dm755 symlink-check $(MELANGE_INSTALL_PATH)/symlink-check \ No newline at end of file + install -Dm755 symlink-check $(MELANGE_INSTALL_PATH)/symlink-check diff --git a/syspeek-tool/README.md b/syspeek-tool/README.md index ceab8d66..ec5c7223 100644 --- a/syspeek-tool/README.md +++ b/syspeek-tool/README.md @@ -2,13 +2,16 @@ The `syspeek` tool statically analyses an ELF binary by disassembling and reporting a syscall profile. -The syscall profile can then be compared to a one dynamically generated when running functional tests for the same application executable. +The syscall profile can then be compared to a one dynamically generated when running functional tests +for the same application executable. -The only application type supported are ones compiled. Script and application that use interpreted languages are not supported by this method. +The only application type supported are ones compiled. Script and application that use interpreted languages +are not supported by this method. ## Requirements Runtime requirements: + - binutils (`objdump` tool) - syscall table file (`/usr/include/asm/unistd_64.h` by default) - `objdump` compiled for the same architecture of the target executable @@ -55,6 +58,8 @@ clock_gettime ## Limitations -There are natural limitations on the static analysis this command does of syscall parameters, due to the nature of the stack and the architecture-specific calling conventions. +There are natural limitations on the static analysis this command does of syscall parameters, +due to the nature of the stack and the architecture-specific calling conventions. -Furthermore, some language compilers embeds the runtime into the binary, like Go does. Consequently it requires to filter out runtime's sycalls. +Furthermore, some language compilers embeds the runtime into the binary, like Go does. +Consequently it requires to filter out runtime's sycalls. diff --git a/tests/README.md b/tests/README.md index 8702aeca..da961be0 100644 --- a/tests/README.md +++ b/tests/README.md @@ -4,7 +4,7 @@ This directory contains all pipeline validation tests for the `test/tw/` pipelin ## Directory Structure -``` +```text tests/ ├── suites/ # Declarative test definitions (consumed by runner) │ ├── docs.yaml @@ -28,7 +28,9 @@ tests/ ### 1. Suite Tests (declarative, auto-generated) -Suite tests live in `suites/` and use a simple declarative YAML format. The test runner reads these definitions, auto-generates melange configs, executes them, and validates results. This is the **default and preferred way** to test pipelines. +Suite tests live in `suites/` and use a simple declarative YAML format. The test runner reads these +definitions, auto-generates melange configs, executes them, and validates results. This is the +**default and preferred way** to test pipelines. Each file maps 1:1 to a pipeline under `pipelines/test/tw/`. For example, `suites/docs.yaml` tests the `test/tw/docs` pipeline. @@ -56,9 +58,12 @@ See the [runner README](runner/README.md) for full details on the test case form ### 2. Manual Tests (hand-written melange YAML) -Manual tests live in `manual/` and are full melange YAML files with subpackages that create synthetic package content. These are built with `melange build` and then tested with `melange test`. +Manual tests live in `manual/` and are full melange YAML files with subpackages that create synthetic package +content. These are built with `melange build` and then tested with `melange test`. -**When to use:** Only when you need to create synthetic packages with specific file layouts that don't exist in Wolfi. For example, testing `header-check` with deliberately malformed headers, or testing edge cases that require precise control over package contents. +**When to use:** Only when you need to create synthetic packages with specific file layouts that don't exist in +Wolfi. For example, testing `header-check` with deliberately malformed headers, or testing edge cases that +require precise control over package contents. See [Writing Manual Tests](#writing-manual-tests) below for the format. @@ -81,6 +86,7 @@ make test-all ### Prerequisites Tests require: + - `melange` binary in your PATH - A signing key (auto-generated via `make build` if missing) - A built `tw` package (`make build` handles this) @@ -109,12 +115,12 @@ testcases: expect_pass: false ``` -2. Run `make test-suite` to verify. +1. Run `make test-suite` to verify. ### Test Case Fields | Field | Required | Description | -|-------|----------|-------------| +| ----- | -------- | ----------- | | `name` | Yes | Descriptive name for the test case | | `description` | No | Detailed explanation | | `package` | Yes | Real Wolfi package to test against | @@ -164,7 +170,7 @@ subpackages: - uses: test/tw/ ``` -2. Run `make test-manual` to verify. +1. Run `make test-manual` to verify. ### Writing Manual Tests @@ -188,7 +194,7 @@ fi echo "PASS: Correctly rejected" ``` -4. **Add tool binaries to test environment** — negative tests invoke checkers manually: +1. **Add tool binaries to test environment** — negative tests invoke checkers manually: ```yaml test: @@ -205,4 +211,4 @@ All build artifacts are written to `tests/.out/` (gitignored): - `.out/generated/` — auto-generated melange configs from suite tests - `.out/packages/` — built packages from manual tests -Run `make clean` to remove all artifacts. \ No newline at end of file +Run `make clean` to remove all artifacts. diff --git a/tests/runner/README.md b/tests/runner/README.md index 6caed2d1..0e28e258 100644 --- a/tests/runner/README.md +++ b/tests/runner/README.md @@ -1,10 +1,12 @@ # Pipeline Test Runner -A Go-based test runner that automatically generates Melange test configurations from declarative test case definitions and executes them via the Melange CLI. +A Go-based test runner that automatically generates Melange test configurations from declarative test case +definitions and executes them via the Melange CLI. ## Overview -This tool eliminates the need to manually write duplicate Melange YAML files for testing pipelines. Instead, you define test cases in a simple YAML format, and the runner: +This tool eliminates the need to manually write duplicate Melange YAML files for testing pipelines. Instead, +you define test cases in a simple YAML format, and the runner: 1. Parses test case definitions from YAML files 2. Validates test suites and test cases @@ -41,6 +43,7 @@ make build-test-runner ``` **Normal mode output:** + - Shows test suite progress - Shows test pass/fail results - Hides melange command output (clean logs) @@ -58,6 +61,7 @@ make build-test-runner ``` **Debug mode output:** + - Shows all normal mode output - Shows [DEBUG] messages with internal details - Shows full melange command output in real-time @@ -88,6 +92,7 @@ The test suite name comes from the filename (e.g., `docs.yaml` → `docs`). ``` This generates all melange YAML files in the output directory without executing tests. Useful for: + - Inspecting generated configurations - Debugging test definitions - Understanding what will be tested @@ -102,10 +107,12 @@ This generates all melange YAML files in the output directory without executing ## Command-Line Options ### Required Flags + - `--test-dir`: Directory containing test suite YAML files (the `suites/` directory) - `--pipeline-dir`: Directory containing pipeline definitions ### Optional Flags + - `--arch`: Architecture to test (default: `x86_64`) - `--repositories`: Comma-separated list of package repositories - `--keyrings`: Comma-separated list of signing key paths @@ -121,7 +128,7 @@ This generates all melange YAML files in the output directory without executing The runner operates within the following layout: -``` +```text tests/ ├── suites/ # Declarative test definitions (consumed by runner) │ ├── docs.yaml @@ -141,8 +148,11 @@ tests/ └── .gitignore ``` -- **`suites/`** — The primary input. Each YAML file defines a test suite for one pipeline. File names match pipeline names 1:1 (e.g., `docs.yaml` tests `test/tw/docs`). -- **`manual/`** — Hand-written melange YAML files for edge cases that need synthetic packages (e.g., creating fake headers to test `header-check`). These are run directly via `melange build` + `melange test`, not through the runner. +- **`suites/`** — The primary input. Each YAML file defines a test suite for one pipeline. File names match +pipeline names 1:1 (e.g., `docs.yaml` tests `test/tw/docs`). +- **`manual/`** — Hand-written melange YAML files for edge cases that need synthetic packages (e.g., creating +fake headers to test `header-check`). These are run directly via `melange build` + `melange test`, not through +the runner. - **`.out/`** — All build artifacts land here. Gitignored. `make clean` removes it entirely. ## Test Case Format @@ -181,6 +191,7 @@ testcases: ### Pipeline Configuration Each pipeline can have: + - **uses** (required): Path to the pipeline (e.g., `test/tw/docs`) - **with** (optional): Parameters to pass to the pipeline @@ -188,7 +199,7 @@ Each pipeline can have: The runner generates configs in separate directories for positive and negative tests: -``` +```text .out/generated/ ├── pass-{sanitized-suite-name}/ │ ├── package1.yaml @@ -199,6 +210,7 @@ The runner generates configs in separate directories for positive and negative t ``` Suite names are sanitized for directory names: + - "Docs Pipeline Tests" → `docs-pipeline-tests` - Lowercase, spaces to hyphens, special chars removed @@ -237,6 +249,7 @@ test: ``` Key features: + - **Auto-generated header**: Clearly marked as generated - **Package name**: Matches the package being tested (1:1 mapping) - **Version**: Always `0.0.0` (not built, only tested) @@ -250,7 +263,7 @@ Key features: Shows clean, focused output: -``` +```console Found 4 test suite files Processing test suite: tests/suites/docs.yaml Test Suite: Docs pipeline validation tests @@ -273,7 +286,7 @@ Test Results: Shows everything including melange output: -``` +```console Processing test suite: tests/suites/docs.yaml [DEBUG] Loading test suite from: tests/suites/docs.yaml Test Suite: Docs pipeline validation tests @@ -307,6 +320,7 @@ Test Suite: Docs pipeline validation tests ## Integration with Melange This runner executes the `melange` CLI binary as a subprocess. This approach: + - Uses the same melange binary as the rest of the build system - Ensures consistent behavior with manual melange invocations - Avoids dependency management complexity @@ -319,7 +333,7 @@ In normal mode, the runner captures melange output. In debug mode, stdout/stderr When tests fail, the runner provides detailed information: -``` +```console ✗ FAIL: Invalid docs package bash Package: bash Expected: fail @@ -353,6 +367,7 @@ To debug failures: ## Context Cancellation The runner supports graceful cancellation: + - Responds to `Ctrl+C` interrupts - Checks for context cancellation in loops - Cleans up properly on interruption @@ -360,6 +375,7 @@ The runner supports graceful cancellation: ## Validation The runner validates: + - **Test suites**: Must have name and test cases - **Test cases**: Must have name, package, and pipelines - **Pipelines**: Must have `uses` field defined @@ -372,7 +388,8 @@ Validation errors include file paths and specific issues for easy debugging. ### Building with Version Info ```bash -go build -ldflags="-X main.commit=$(git rev-parse HEAD) -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" -o pipeline-runner . +go build -ldflags="-X main.commit=$(git rev-parse HEAD) \ +-X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" -o pipeline-runner . ``` ### Running Tests @@ -403,17 +420,20 @@ go build -ldflags="-X main.commit=$(git rev-parse HEAD) -X main.date=$(date -u + ### Test fails unexpectedly 1. Generate and inspect the config: + ```bash ./pipeline-runner --generate-only --test-dir ../suites --pipeline-dir ../../pipelines --out-dir ../.out/generated cat ../.out/generated/pass-suite-name/package.yaml ``` 2. Run in debug mode: + ```bash ./pipeline-runner --debug --test-dir ../suites --pipeline-dir ../../pipelines --out-dir ../.out/generated ``` 3. Run melange directly (from the repo root): + ```bash melange test --debug \ --arch $(uname -m) \ diff --git a/tests/runner/go.mod b/tests/runner/go.mod index aa376e33..c2f1dc6b 100644 --- a/tests/runner/go.mod +++ b/tests/runner/go.mod @@ -2,4 +2,4 @@ module github.com/chainguard-dev/tw/tests/runner go 1.24.6 -require gopkg.in/yaml.v3 v3.0.1 \ No newline at end of file +require gopkg.in/yaml.v3 v3.0.1 diff --git a/tw-pip-check/Makefile~ b/tw-pip-check/Makefile~ new file mode 100644 index 00000000..86ed16ab --- /dev/null +++ b/tw-pip-check/Makefile~ @@ -0,0 +1,15 @@ +PROJECT = ldd-check +MELANGE_CONTEXTDIR ?= /tmp/melange-context/$(PROJECT) +MELANGE_INSTALL_PATH = $(MELANGE_CONTEXTDIR)/usr/bin + +.PHONY: build melange-install + +build: + chmod +x ldd-check + +test: + +melange-install: build + echo $@ + mkdir -p $(MELANGE_INSTALL_PATH) + install -Dm755 $(PROJECT) $(MELANGE_INSTALL_PATH)/$(PROJECT) diff --git a/tw-pip-check/README.md~ b/tw-pip-check/README.md~ new file mode 100644 index 00000000..fc919bc9 --- /dev/null +++ b/tw-pip-check/README.md~ @@ -0,0 +1,3 @@ +# ldd-check + +This is called to run `ldd` on package contents or files. diff --git a/tw-pip-check/tw-pip-check~ b/tw-pip-check/tw-pip-check~ new file mode 100644 index 00000000..e69de29b diff --git a/tw/pkg/commands/shelldeps/README.md b/tw/pkg/commands/shelldeps/README.md index cb19fd8b..951224e8 100644 --- a/tw/pkg/commands/shelldeps/README.md +++ b/tw/pkg/commands/shelldeps/README.md @@ -1,6 +1,7 @@ # shell-deps -The `shell-deps` command analyzes shell scripts (bash, dash, or sh) and lists external programs (dependencies) that the shell script may invoke. It can also detect GNU coreutils-specific flags that don't work with busybox. +The `shell-deps` command analyzes shell scripts (bash, dash, or sh) and lists external programs (dependencies) that +the shell script may invoke. It can also detect GNU coreutils-specific flags that don't work with busybox. ## Key Features @@ -13,7 +14,8 @@ The `shell-deps` command analyzes shell scripts (bash, dash, or sh) and lists ex ## Overview -`shell-deps` uses the [mvdan.cc/sh/v3](https://github.com/mvdan/sh) parser to analyze shell scripts and identify external command dependencies. It correctly excludes: +`shell-deps` uses the [mvdan.cc/sh/v3](https://github.com/mvdan/sh) parser to analyze shell scripts and identify +external command dependencies. It correctly excludes: - Shell built-in commands (e.g., `echo`, `cd`, `test`, `[`) - Functions defined within the script @@ -43,6 +45,7 @@ tw shell-deps show [flags] file [file...] ``` **Flags:** + - `--path=PATH` - PATH-like colon-separated directories to check for missing commands (e.g., `/usr/bin:/usr/local/bin`) **Examples:** @@ -63,14 +66,15 @@ tw shell-deps show --json script.sh **Example Output:** -``` +```yaml script.sh: deps: awk grep sed shell: /bin/sh ``` With `--path=/usr/bin`: -``` + +```yaml script.sh: deps: awk bobob grep shell: /bin/bash @@ -86,13 +90,16 @@ tw shell-deps scan [flags] search-dir ``` **Flags:** + - `--missing=path/` - Path to directory containing available executables -- `--match=regex` - Regular expression pattern to match additional files as shell scripts (e.g., `\.makefile$` to include files ending in `.makefile`) +- `--match=regex` - Regular expression pattern to match additional files as shell scripts (e.g., `\.makefile$` to +include files ending in `.makefile`) - `-x, --executable` - Only consider executable files as shell scripts **Shell Script Detection:** By default, `scan` identifies shell scripts by checking for these shebangs: + - `#!/bin/sh` - `#!/bin/dash` - `#!/bin/bash` @@ -100,7 +107,7 @@ By default, `scan` identifies shell scripts by checking for these shebangs: - `#!/usr/bin/env dash` - `#!/usr/bin/env bash` -Both `#!` and `#! ` (with space) variations are supported. +Both `#!` and `#!` (with space) variations are supported. **Examples:** @@ -134,16 +141,20 @@ tw shell-deps check [flags] file [file...] ``` **Flags:** + - `--path=PATH` - PATH-like colon-separated directories to search for commands (default: `/usr/bin:/usr/local/bin`) - `--strict` - Exit with non-zero status if any issues are found (default: `true`) This command performs two types of checks: + 1. **Missing dependencies** - Commands that don't exist in the specified PATH 2. **GNU compatibility** - Detects GNU coreutils-specific flags that won't work with busybox -The GNU compatibility check automatically determines whether commands are provided by busybox or coreutils by examining symlinks in the PATH. +The GNU compatibility check automatically determines whether commands are provided by busybox or coreutils by +examining symlinks in the PATH. **Key Feature:** The output shows ALL dependencies found, categorized as: + - ✓ **available** - Commands found in PATH - ✗ **missing** - Commands not found in PATH - ⚠ **gnu-required** - Commands that need GNU coreutils (not busybox) @@ -163,7 +174,7 @@ tw shell-deps check --path=/usr/bin:/usr/local/bin /opt/scripts/*.sh **Example Output:** -``` +```console Dependency Check Results ======================== Analyzed: 2 shell script(s) @@ -204,11 +215,13 @@ tw shell-deps check-package [flags] ``` **Flags:** + - `--path=PATH` - PATH-like colon-separated directories to search for commands (default: `/usr/bin:/bin`) - `--strict` - Exit with non-zero status if any issues are found (default: `true`) - `--package-dir=DIR` - Directory to search for package YAML files for runtime dependency lookup (default: `.`) This command: + 1. Gets the list of files installed by the package using `apk info -L` 2. Filters for shell scripts among the installed files 3. Analyzes each script's dependencies @@ -233,7 +246,7 @@ tw shell-deps check-package --json nginx **Example Output:** -``` +```console Package: vim Found 2034 installed file(s) Runtime dependencies: [] @@ -263,6 +276,7 @@ Checked 5 script(s) ### What is Detected The parser identifies external commands from: + - Direct command invocations: `grep pattern file.txt` - Command substitutions: `out=$(awk '{print $1}' file)` - Pipes: `cat file | grep pattern | awk '{print $1}'` @@ -272,7 +286,8 @@ The parser identifies external commands from: ### Wrapper Function Detection -The parser automatically identifies "wrapper functions" - functions that execute their arguments. This is a common pattern for logging or error handling: +The parser automatically identifies "wrapper functions" - functions that execute their arguments. This is a common +pattern for logging or error handling: ```bash #!/bin/sh @@ -285,55 +300,64 @@ vr ls /etc # 'ls' is detected as a dependency vr grep foo bar # 'grep' is detected as a dependency ``` -A function is identified as a wrapper if it contains `"$@"` or `$@` in command position. The first argument passed to such functions is analyzed as a potential external command. +A function is identified as a wrapper if it contains `"$@"` or `$@` in command position. The first argument +passed to such functions is analyzed as a potential external command. ### What is Excluded The following are **not** considered external dependencies: **Shell Built-ins:** -- POSIX special built-ins: `break`, `:`, `continue`, `.`, `eval`, `exec`, `exit`, `export`, `readonly`, `return`, `set`, `shift`, `times`, `trap`, `unset` -- POSIX regular built-ins: `alias`, `bg`, `cd`, `command`, `false`, `fc`, `fg`, `getopts`, `jobs`, `kill`, `pwd`, `read`, `true`, `umask`, `unalias`, `wait`, `hash`, `type`, `ulimit`, `[`, `test`, `echo`, `printf` + +- POSIX special built-ins: `break`, `:`, `continue`, `.`, `eval`, `exec`, `exit`, `export`, `readonly`, `return`, +`set`, `shift`, `times`, `trap`, `unset` +- POSIX regular built-ins: `alias`, `bg`, `cd`, `command`, `false`, `fc`, `fg`, `getopts`, `jobs`, `kill`, `pwd`, +`read`, `true`, `umask`, `unalias`, `wait`, `hash`, `type`, `ulimit`, `[`, `test`, `echo`, `printf` - Bash/dash additional built-ins: `source`, `local`, `declare`, `typeset`, `let`, `enable`, `builtin`, and others **Script-defined entities:** + - Functions defined in the script - Aliases defined in the script **Control structures:** + - `if`, `then`, `else`, `elif`, `fi`, `while`, `do`, `done`, `for`, `in`, `case`, `esac`, `until`, `select` ## GNU Coreutils Compatibility -The `check` and `check-package` commands detect GNU coreutils-specific flags that don't work with busybox. This is critical for Wolfi/Chainguard packages where busybox is often used instead of full coreutils. +The `check` and `check-package` commands detect GNU coreutils-specific flags that don't work with busybox. This is +critical for Wolfi/Chainguard packages where busybox is often used instead of full coreutils. ### Detected GNU-only Flags -| Command | GNU-only Flags | -|------------|----------------------------------------------------------| +| Command | GNU-only Flags | +| ------- | -------------- | | `realpath` | `--no-symlinks`, `--relative-base`, `--relative-to`, `-q`, `--quiet` | -| `stat` | `--format`, `--printf` | -| `cp` | `--reflink`, `--sparse` | -| `date` | `--iso-8601`, `-I` | -| `mktemp` | `--suffix` | -| `sort` | `-h`, `--human-numeric-sort` | -| `ls` | `--time-style` | -| `df` | `--output` | +| `stat` | `--format`, `--printf` | +| `cp` | `--reflink`, `--sparse` | +| `date` | `--iso-8601`, `-I` | +| `mktemp` | `--suffix` | +| `sort` | `-h`, `--human-numeric-sort` | +| `ls` | `--time-style` | +| `df` | `--output` | | `readlink` | `-e`, `--canonicalize-existing`, `-m`, `--canonicalize-missing` | -| `tail` | `--pid` | -| `touch` | `--date` | -| `head` | `--bytes` | -| `du` | `--apparent-size` | -| `chmod` | `--reference` | -| `chown` | `--reference` | -| `install` | `-D` (creates parent directories) | -| `tr` | `--complement` | -| `wc` | `--total` | -| `seq` | `--equal-width` | +| `tail` | `--pid` | +| `touch` | `--date` | +| `head` | `--bytes` | +| `du` | `--apparent-size` | +| `chmod` | `--reference` | +| `chown` | `--reference` | +| `install` | `-D` (creates parent directories) | +| `tr` | `--complement` | +| `wc` | `--total` | +| `seq` | `--equal-width` | ### Auto-detection of Providers -The `check` command automatically determines whether a command is provided by busybox or coreutils by examining symlinks in the PATH. If a command (e.g., `/usr/bin/chmod`) is a symlink to busybox, GNU-specific flags will be flagged. If it points to a real coreutils binary, no warning is issued. +The `check` command automatically determines whether a command is provided by busybox or coreutils by examining +symlinks in the PATH. If a command (e.g., `/usr/bin/chmod`) is a symlink to busybox, GNU-specific flags will be +flagged. If it points to a real coreutils binary, no warning is issued. ## Example Script Analysis @@ -354,13 +378,15 @@ fi ``` Output: -``` + +```yaml script.sh: deps: /sbin/sudo awk bobob grep shell: /bin/sh ``` -**Note:** `stderr` is excluded (it's a function), `echo`, `test`, and `[` are excluded (built-ins), but `grep`, `awk`, `bobob`, and `/sbin/sudo` are included as external dependencies. +**Note:** `stderr` is excluded (it's a function), `echo`, `test`, and `[` are excluded (built-ins), but `grep`, +`awk`, `bobob`, and `/sbin/sudo` are included as external dependencies. ## JSON Output Format @@ -402,6 +428,7 @@ When using `--json`, the output is structured as follows: ``` Fields: + - `file` - Path to the script - `deps` - List of external dependencies (sorted alphabetically) - `shell` - The shell interpreter from the shebang (e.g., `/bin/bash`, `bash`) @@ -414,7 +441,8 @@ Fields: - `0` - Success (all scripts parsed successfully, no issues in strict mode) - `1` - Errors occurred while processing one or more files, or issues found in `--strict` mode -When errors occur, the error messages are included in the output, and the command exits with code 1 after processing all files. +When errors occur, the error messages are included in the output, and the command exits with code 1 after +processing all files. ## Use Cases @@ -429,5 +457,6 @@ When errors occur, the error messages are included in the output, and the comman - **Parser:** Uses `mvdan.cc/sh/v3` for robust shell script parsing - **Language Support:** Supports POSIX sh, bash, and dash syntax -- **Performance:** Scripts are parsed once; dependencies are extracted in two passes (first to identify functions/aliases/wrappers, second to identify commands) +- **Performance:** Scripts are parsed once; dependencies are extracted in two passes (first to identify +functions/aliases/wrappers, second to identify commands) - **GNU Detection:** Uses symlink analysis to determine if commands are provided by busybox or coreutils diff --git a/tw/testdata/dgrep.txtar b/tw/testdata/dgrep.txtar index 2c3a5429..90e055ef 100644 --- a/tw/testdata/dgrep.txtar +++ b/tw/testdata/dgrep.txtar @@ -8,4 +8,4 @@ dgrep mysql --std-errors --ne-exclude 'panic' dgrep app-container --ne 'FATAL' --ne 'panic' dgrep worker -e 'processing' -i -r 1 dgrep api-server -e 'health.*check' -v -dgrep cache-server --ne 'connection.*refused' \ No newline at end of file +dgrep cache-server --ne 'connection.*refused' diff --git a/usrmerge-tool/README.md b/usrmerge-tool/README.md index 56c08091..7c322c5b 100644 --- a/usrmerge-tool/README.md +++ b/usrmerge-tool/README.md @@ -6,4 +6,6 @@ This is function for a usrMerge tool. At this point we would call - ./usrmerge --sbin ${{targets.contextdir}} +```bash +./usrmerge --sbin ${{targets.contextdir}} +```