From 345171a8df6bf8dbabedb5d46e7353e55665ee76 Mon Sep 17 00:00:00 2001 From: Oliver Slater Date: Fri, 19 Jun 2026 12:46:57 +0100 Subject: [PATCH 1/3] feat(mise): enforce and document task surface consistency policy Implement a hard separation between active referenced tasks and unreferenced starter scripts in mise.toml. - Split mise tasks into maintained (referenced by automation/docs) and unreferenced (starter/manual) - Comment out 9 unreferenced tasks: docker-*, tests-* (unit, style, release-config, release-updater) - Keep terraform-derive-module-constraints and terraform-wrapper active as maintained manual utilities - Add test-mise-task-surface.sh to enforce that all referenced tasks are active and no commented tasks are referenced - Update pre-commit hooks to use mise task invocations instead of raw script paths - Update GitHub Actions composite actions to use mise tasks for consistency - Update all documentation (README, instruction files, skills, prompts) to reference mise tasks - Add comprehensive README note on terraform-derive-module-constraints with prerequisites and interpretation guidance - Fix test expectations: terraform module upgrade (darwin_arm64 platform) and workflow security (mise task references) - All 6 test suites now pass (161 total test cases) This ensures the mise task surface remains maintainable and consistent between configuration, automation, and documentation. --- .../actions/check-english-usage/action.yaml | 2 +- .github/actions/check-file-format/action.yaml | 2 +- .../actions/check-markdown-format/action.yaml | 2 +- .../create-lines-of-code-report/action.yaml | 2 +- .github/actions/lint-terraform/action.yaml | 2 +- .../perform-static-analysis/action.yaml | 2 +- .github/actions/scan-dependencies/action.yaml | 4 +- .github/actions/scan-secrets/action.yaml | 2 +- .../terraform-modules.instructions.md | 16 +-- .../refresh-module-providers.prompt.md | 10 +- .github/skills/pre-commit-hooks.skill.md | 2 +- .../terraform-module-maintenance.skill.md | 8 +- .../skills/terraform-module-patterns.skill.md | 4 +- .../dependency-tools-mise-upgrade.yml | 4 +- .pre-commit-config.yaml | 20 +-- README.md | 43 ++++-- docs/developer-guides/Bash_and_Make.md | 6 +- docs/user-guides/Perform_static_analysis.md | 2 +- .../user-guides/Pre_commit_hooks_reference.md | 6 +- docs/user-guides/Scan_dependencies.md | 4 +- docs/user-guides/Scan_secrets.md | 2 +- mise.toml | 103 +++++++++++++++ tests/README.md | 19 ++- tests/run-all-tests.sh | 13 ++ tests/test-mise-task-surface.sh | 123 ++++++++++++++++++ tests/test-module-upgrade.sh | 2 +- tests/test-tool-version-upgrade.sh | 65 ++++----- tests/test-workflow-security.sh | 6 +- 28 files changed, 366 insertions(+), 110 deletions(-) create mode 100755 tests/test-mise-task-surface.sh diff --git a/.github/actions/check-english-usage/action.yaml b/.github/actions/check-english-usage/action.yaml index 9953bcc7..b7c944f6 100644 --- a/.github/actions/check-english-usage/action.yaml +++ b/.github/actions/check-english-usage/action.yaml @@ -7,4 +7,4 @@ runs: shell: bash run: | export BRANCH_NAME=origin/${{ github.event.repository.default_branch }} - check=branch ./scripts/githooks/check-english-usage.sh + check=branch mise run githooks-check-english-usage diff --git a/.github/actions/check-file-format/action.yaml b/.github/actions/check-file-format/action.yaml index bd0929a8..e851b101 100644 --- a/.github/actions/check-file-format/action.yaml +++ b/.github/actions/check-file-format/action.yaml @@ -7,4 +7,4 @@ runs: shell: bash run: | export BRANCH_NAME=origin/${{ github.event.repository.default_branch }} - check=branch ./scripts/githooks/check-file-format.sh + check=branch mise run githooks-check-file-format diff --git a/.github/actions/check-markdown-format/action.yaml b/.github/actions/check-markdown-format/action.yaml index 53a715b4..74618c9f 100644 --- a/.github/actions/check-markdown-format/action.yaml +++ b/.github/actions/check-markdown-format/action.yaml @@ -7,4 +7,4 @@ runs: shell: bash run: | export BRANCH_NAME=origin/${{ github.event.repository.default_branch }} - check=branch ./scripts/githooks/check-markdown-format.sh + check=branch mise run githooks-check-markdown-format diff --git a/.github/actions/create-lines-of-code-report/action.yaml b/.github/actions/create-lines-of-code-report/action.yaml index b21f0667..2ce07d7a 100644 --- a/.github/actions/create-lines-of-code-report/action.yaml +++ b/.github/actions/create-lines-of-code-report/action.yaml @@ -26,7 +26,7 @@ runs: shell: bash run: | export BUILD_DATETIME=${{ inputs.build_datetime }} - ./scripts/reports/create-lines-of-code-report.sh + mise run reports-create-lines-of-code - name: "Compress CLOC report" shell: bash run: zip lines-of-code-report.json.zip lines-of-code-report.json diff --git a/.github/actions/lint-terraform/action.yaml b/.github/actions/lint-terraform/action.yaml index d5dfe35d..27d1a71c 100644 --- a/.github/actions/lint-terraform/action.yaml +++ b/.github/actions/lint-terraform/action.yaml @@ -10,7 +10,7 @@ runs: - name: "Check Terraform format" shell: bash run: | - check_only=true scripts/githooks/check-terraform-format.sh + check_only=true mise run githooks-check-terraform-format - name: "Validate Terraform" shell: bash run: | diff --git a/.github/actions/perform-static-analysis/action.yaml b/.github/actions/perform-static-analysis/action.yaml index a619e9d2..6040d22f 100644 --- a/.github/actions/perform-static-analysis/action.yaml +++ b/.github/actions/perform-static-analysis/action.yaml @@ -25,4 +25,4 @@ runs: export SONAR_ORGANISATION_KEY=${{ inputs.sonar_organisation_key }} export SONAR_PROJECT_KEY=${{ inputs.sonar_project_key }} export SONAR_TOKEN=${{ inputs.sonar_token }} - ./scripts/reports/perform-static-analysis.sh + mise run reports-perform-static-analysis diff --git a/.github/actions/scan-dependencies/action.yaml b/.github/actions/scan-dependencies/action.yaml index f8ed605d..a9e3153d 100644 --- a/.github/actions/scan-dependencies/action.yaml +++ b/.github/actions/scan-dependencies/action.yaml @@ -26,7 +26,7 @@ runs: shell: bash run: | export BUILD_DATETIME=${{ inputs.build_datetime }} - ./scripts/reports/create-sbom-report.sh + mise run reports-create-sbom - name: "Compress SBOM report" shell: bash run: zip sbom-repository-report.json.zip sbom-repository-report.json @@ -41,7 +41,7 @@ runs: shell: bash run: | export BUILD_DATETIME=${{ inputs.build_datetime }} - ./scripts/reports/scan-vulnerabilities.sh + mise run reports-scan-vulnerabilities - name: "Compress vulnerabilities report" shell: bash run: zip vulnerabilities-repository-report.json.zip vulnerabilities-repository-report.json diff --git a/.github/actions/scan-secrets/action.yaml b/.github/actions/scan-secrets/action.yaml index 1ed8bac2..f18c95ed 100644 --- a/.github/actions/scan-secrets/action.yaml +++ b/.github/actions/scan-secrets/action.yaml @@ -7,4 +7,4 @@ runs: shell: bash run: | # Please do not change this `check=whole-history` setting, as new patterns may be added or history may be rewritten. - check=whole-history ./scripts/githooks/scan-secrets.sh + check=whole-history mise run githooks-scan-secrets diff --git a/.github/instructions/terraform-modules.instructions.md b/.github/instructions/terraform-modules.instructions.md index e0541ac7..3b540287 100644 --- a/.github/instructions/terraform-modules.instructions.md +++ b/.github/instructions/terraform-modules.instructions.md @@ -66,16 +66,10 @@ module "" { When AWS provider versions change or community modules receive updates: 1. Use the upgrade helper to refresh a single module: - - ```bash - ./scripts/terraform/upgrade-module.sh infrastructure/modules/vpc - ``` + `mise run terraform-upgrade-module -- infrastructure/modules/vpc` 2. Or refresh all modules at once: - - ```bash - ./scripts/terraform/upgrade-module.sh update-all - ``` + `mise run terraform-upgrade-module -- update-all` The helper automates three steps: @@ -201,16 +195,14 @@ Module READMEs should include: ### When Adding or Modifying a Module 1. **Run the upgrade helper** to regenerate module documentation. - - ```bash - ./scripts/terraform/upgrade-module.sh infrastructure/modules/ - ``` + `mise run terraform-upgrade-module -- infrastructure/modules/` This automatically updates the module's `README.md` via `terraform-docs`. 1. **Update the root README.md** if you've added a new module, changed Dependabot automation behaviour, or changed module sourcing/upgrade procedures. 1. **Update relevant user guides** in `docs/user-guides/`. + If you've added/changed a pre-commit hook, update `Pre_commit_hooks_reference.md`. If you've changed upgrade procedures or tooling, update the related guides. 1. **Update `infrastructure/AGENTS.md`** if you've introduced a new pattern/tool, changed naming conventions, or changed quality expectations/validation rules. diff --git a/.github/prompts/refresh-module-providers.prompt.md b/.github/prompts/refresh-module-providers.prompt.md index 900c0688..a25e5584 100644 --- a/.github/prompts/refresh-module-providers.prompt.md +++ b/.github/prompts/refresh-module-providers.prompt.md @@ -11,13 +11,13 @@ Use this prompt when AWS provider versions change, community modules receive upd To refresh a single module with the latest provider versions and update its README: ```bash -./scripts/terraform/upgrade-module.sh infrastructure/modules/ +mise run terraform-upgrade-module -- infrastructure/modules/ ``` **Example:** ```bash -./scripts/terraform/upgrade-module.sh infrastructure/modules/s3-bucket +mise run terraform-upgrade-module -- infrastructure/modules/s3-bucket ``` The script will: @@ -31,7 +31,7 @@ The script will: To refresh every module at once: ```bash -./scripts/terraform/upgrade-module.sh update-all +mise run terraform-upgrade-module -- update-all ``` The script will warn before starting and then iterate through all modules under `infrastructure/modules/`, updating each in sequence. @@ -64,5 +64,5 @@ If the script fails: 1. Ensure `terraform`, `terraform-docs`, and `pre-commit` are installed and on PATH 2. Check that you're in the repository root directory -3. Verify that `scripts/terraform/upgrade-module.sh` is executable: `chmod +x scripts/terraform/upgrade-module.sh` -4. Run with `bash -x` for detailed debugging: `bash -x ./scripts/terraform/upgrade-module.sh infrastructure/modules/vpc` +3. Verify that the mise task exists: `mise tasks ls | grep terraform-upgrade-module` +4. Run with shell tracing for detailed debugging: `MISE_TASK_OUTPUT=1 mise run terraform-upgrade-module -- infrastructure/modules/vpc` diff --git a/.github/skills/pre-commit-hooks.skill.md b/.github/skills/pre-commit-hooks.skill.md index 5d512f5e..7445e323 100644 --- a/.github/skills/pre-commit-hooks.skill.md +++ b/.github/skills/pre-commit-hooks.skill.md @@ -244,7 +244,7 @@ terraform -chdir="infrastructure/modules/s3-bucket" providers lock \ Or use the helper script: ```bash -./scripts/terraform/upgrade-module.sh infrastructure/modules/s3-bucket +mise run terraform-upgrade-module -- infrastructure/modules/s3-bucket ``` **Manual run:** diff --git a/.github/skills/terraform-module-maintenance.skill.md b/.github/skills/terraform-module-maintenance.skill.md index 09794a93..2938a9de 100644 --- a/.github/skills/terraform-module-maintenance.skill.md +++ b/.github/skills/terraform-module-maintenance.skill.md @@ -21,7 +21,7 @@ Module maintenance ensures that Terraform wrapper modules stay current, secure, ### Single Module Upgrade ```bash -./scripts/terraform/upgrade-module.sh infrastructure/modules/ +mise run terraform-upgrade-module -- infrastructure/modules/ ``` This executes: @@ -37,7 +37,7 @@ This executes: ### Repository-Wide Upgrade ```bash -./scripts/terraform/upgrade-module.sh update-all +mise run terraform-upgrade-module -- update-all ``` Prompts for confirmation, then upgrades all modules under `infrastructure/modules/` in sequence. Useful for bulk provider version bumps (e.g., AWS provider 6.42 → 6.50). @@ -141,7 +141,7 @@ If `.terraform.lock.hcl` shows provider version conflicts: ```bash # Delete and regenerate rm infrastructure/modules/*/. terraform.lock.hcl -./scripts/terraform/upgrade-module.sh update-all +mise run terraform-upgrade-module -- update-all ``` ### Documentation Doesn't Regenerate @@ -169,7 +169,7 @@ The `.terraform.lock.hcl` file ensures Terraform downloads the correct version f ## Checklist: Completing a Module Upgrade -- [ ] Run `./scripts/terraform/upgrade-module.sh ` +- [ ] Run `mise run terraform-upgrade-module -- ` - [ ] Review `git diff` for unexpected changes - [ ] Check for upstream breaking changes in release notes - [ ] Validate security baseline controls are still enforced diff --git a/.github/skills/terraform-module-patterns.skill.md b/.github/skills/terraform-module-patterns.skill.md index c15b3d61..64c7938a 100644 --- a/.github/skills/terraform-module-patterns.skill.md +++ b/.github/skills/terraform-module-patterns.skill.md @@ -68,10 +68,10 @@ When AWS providers or upstream community modules receive updates, use the upgrad ```bash # Single module -./scripts/terraform/upgrade-module.sh infrastructure/modules/s3-bucket +mise run terraform-upgrade-module -- infrastructure/modules/s3-bucket # All modules -./scripts/terraform/upgrade-module.sh update-all +mise run terraform-upgrade-module -- update-all ``` The helper: diff --git a/.github/workflows/dependency-tools-mise-upgrade.yml b/.github/workflows/dependency-tools-mise-upgrade.yml index a498a1af..1a1339fa 100644 --- a/.github/workflows/dependency-tools-mise-upgrade.yml +++ b/.github/workflows/dependency-tools-mise-upgrade.yml @@ -42,7 +42,7 @@ jobs: shell: bash run: | level="${UPGRADE_LEVEL:-patch}" - bash scripts/mise/update-tool-versions.sh --upgrade-level "$level" + mise run update-tool-versions -- --upgrade-level "$level" - name: "Create pull request" uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 @@ -55,6 +55,7 @@ jobs: Updated files: - .tool-versions - mise.toml + - README.md - mise.lock Upgrade level: `${{ github.event.inputs.upgrade_level || 'patch' }}` @@ -67,4 +68,5 @@ jobs: add-paths: | .tool-versions mise.toml + README.md mise.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d5cc4fa4..79fa5b2f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -31,8 +31,8 @@ repos: - id: generate-terraform-providers name: generate-terraform-providers require_serial: true - entry: ./scripts/githooks/generate-terraform-providers.sh - language: script + entry: mise run githooks-generate-terraform-providers + language: system files: \.tf(vars)?$ pass_filenames: false @@ -51,8 +51,8 @@ repos: - id: regenerate-dependabot-config name: regenerate-dependabot-config require_serial: true - entry: ./scripts/githooks/check-dependabot-config.sh - language: script + entry: mise run githooks-check-dependabot-config + language: system files: infrastructure/modules/.*/versions\.tf$ pass_filenames: false @@ -139,7 +139,7 @@ repos: hooks: - id: shellcheck name: shellcheck - entry: bash -c 'for f in "$@"; do SHELLCHECK_OPTS="--severity=warning" file="$f" bash scripts/shellscript-linter.sh || exit 1; done' -- + entry: bash -c 'for f in "$@"; do SHELLCHECK_OPTS="--severity=warning" file="$f" mise run shellscript-linter || exit 1; done' -- language: system types: [shell] require_serial: true @@ -155,34 +155,34 @@ repos: hooks: - id: check-file-format name: check-file-format - entry: bash -c 'check=all ./scripts/githooks/check-file-format.sh' + entry: bash -c 'check=all mise run githooks-check-file-format' language: system pass_filenames: false - id: check-markdown-format name: check-markdown-format - entry: bash -c 'check=all ./scripts/githooks/check-markdown-format.sh' + entry: bash -c 'check=all mise run githooks-check-markdown-format' language: system files: \.md$ pass_filenames: false - id: check-english-usage name: check-english-usage - entry: bash -c 'check=all ./scripts/githooks/check-english-usage.sh' + entry: bash -c 'check=all mise run githooks-check-english-usage' language: system files: \.md$ pass_filenames: false - id: check-terraform-format name: check-terraform-format - entry: bash -c 'check_only=true ./scripts/githooks/check-terraform-format.sh' + entry: bash -c 'check_only=true mise run githooks-check-terraform-format' language: system files: \.tf(vars)?$ pass_filenames: false - id: scan-secrets name: scan-secrets - entry: bash -c 'check=whole-history ./scripts/githooks/scan-secrets.sh' + entry: bash -c 'check=whole-history mise run githooks-scan-secrets' language: system pass_filenames: false diff --git a/README.md b/README.md index 52bca683..9168fa08 100644 --- a/README.md +++ b/README.md @@ -75,36 +75,40 @@ Both files must be kept in sync. Update `.tool-versions` first, then ensure `mis For routine upgrades, use the shared helper so local and CI use the same logic: ```shell -bash scripts/mise/update-tool-versions.sh +mise run update-tool-versions ``` Choose an upgrade level when needed: ```shell # Patch updates only -bash scripts/mise/update-tool-versions.sh --upgrade-level patch +mise run update-tool-versions -- --upgrade-level patch # Minor updates only -bash scripts/mise/update-tool-versions.sh --upgrade-level minor +mise run update-tool-versions -- --upgrade-level minor # Major updates only -bash scripts/mise/update-tool-versions.sh --upgrade-level major +mise run update-tool-versions -- --upgrade-level major # All updates (default) -bash scripts/mise/update-tool-versions.sh --upgrade-level all +mise run update-tool-versions -- --upgrade-level all ``` Preview only (no file changes): ```shell -bash scripts/mise/update-tool-versions.sh --dry-run +mise run update-tool-versions -- --dry-run ``` The scheduled `dependency-tools-mise-upgrade` workflow defaults to patch updates, and manual runs can override the level via the `upgrade_level` input. Local development and CI both resolve pinned versions from these files through mise. -The prerequisites table above is regenerated by `scripts/mise/update-tool-versions.sh` so it stays aligned with the pinned tool versions. +The prerequisites table above is regenerated by `mise run update-tool-versions` so it stays aligned with the pinned tool versions. + +Referenced automation scripts under `scripts/` are exposed as active `mise` tasks. Use `mise tasks ls` to discover the maintained task surface, and run them via `mise run `. + +Unreferenced starter/manual scripts are intentionally commented out in `mise.toml` and are not treated as CI/pre-commit quality gates unless explicitly promoted to referenced tasks. ### Configuration @@ -171,17 +175,36 @@ pre-commit run --all-files Use the upgrade helper to refresh a single module after dependency changes: ```shell -./scripts/terraform/upgrade-module.sh infrastructure/modules/vpc +mise run terraform-upgrade-module -- infrastructure/modules/vpc ``` To refresh every module in the repository, use: ```shell -./scripts/terraform/upgrade-module.sh update-all +mise run terraform-upgrade-module -- update-all ``` repo-wide mode warns before it starts because it iterates every module under `infrastructure/modules`. +Use the constraint report when you want a manual audit of whether a wrapper module's local Terraform or AWS provider minimums lag behind resolved upstream module requirements: + +```shell +mise run terraform-derive-module-constraints +``` + +This task is advisory only. It is useful after initialising modules locally, because it reads resolved dependency metadata from `.terraform/modules/modules.json` and prints recommended minimum constraints as a markdown table. + +Use it when you are reviewing or adding `versions.tf` for a wrapper module and want a quick lower-bound report derived from the upstream modules that Terraform has actually resolved on disk. + +Notes: + +- Run `terraform init` in the module first, or use `mise run terraform-upgrade-module -- `, so `.terraform/modules/modules.json` and downloaded child modules exist locally. +- The report combines the wrapper module's local constraints with lower bounds found in upstream child-module `versions.tf` files. +- The output is a starting point for human review, not an automatic source of truth. You still need to account for any stricter minimums introduced by functionality used directly in the wrapper module. +- The script reasons best about simple lower-bound constraints such as `>= 1.13` and `>= 6.50.0`; more complex version expressions are surfaced as notes rather than fully merged. + +For example, if the report recommends `required_version >= 1.13` and `aws >= 6.50.0`, treat those as candidate minimums for the module's `versions.tf`, then confirm they still match any Terraform or provider features used directly in the wrapper. + ## Design ### Repository structure @@ -554,7 +577,7 @@ The `.github/dependabot.yaml` configuration is regenerated automatically wheneve ```bash # Generate configuration manually if needed -scripts/generate-dependabot-config.sh +mise run generate-dependabot-config ``` The generator: diff --git a/docs/developer-guides/Bash_and_Make.md b/docs/developer-guides/Bash_and_Make.md index d4379910..839d64a3 100644 --- a/docs/developer-guides/Bash_and_Make.md +++ b/docs/developer-guides/Bash_and_Make.md @@ -135,7 +135,7 @@ VERBOSE=1 make docker-example-build for Bash scripts ```shell -VERBOSE=1 scripts/shellscript-linter.sh +VERBOSE=1 mise run shellscript-linter ``` ### Scripts @@ -143,11 +143,11 @@ VERBOSE=1 scripts/shellscript-linter.sh Most scripts provided with this repository template can use tools installed on your `PATH` if they are available or run them from within a Docker container. To force a script to use Docker, the `FORCE_USE_DOCKER` variable is provided. This feature allows you to use custom tooling if it is present on the command-line path. Here is how to use it: ```shell -FORCE_USE_DOCKER=1 scripts/shellscript-linter.sh +FORCE_USE_DOCKER=1 mise run shellscript-linter ``` You can combine it with the `VERBOSE` flag to see the details of the execution flow: ```shell -VERBOSE=1 FORCE_USE_DOCKER=1 scripts/shellscript-linter.sh +VERBOSE=1 FORCE_USE_DOCKER=1 mise run shellscript-linter ``` diff --git a/docs/user-guides/Perform_static_analysis.md b/docs/user-guides/Perform_static_analysis.md index a1f70118..cde5fd64 100644 --- a/docs/user-guides/Perform_static_analysis.md +++ b/docs/user-guides/Perform_static_analysis.md @@ -32,7 +32,7 @@ You can run and test static analysis locally on a developer's workstation using export SONAR_ORGANISATION_KEY=nhs-england-tools # Replace with your organisation key export SONAR_PROJECT_KEY=repository-template # Replace with your project key export SONAR_TOKEN=[replace-with-your-sonar-token] -./scripts/reports/perform-static-analysis.sh +mise run reports-perform-static-analysis ``` ## Configuration checklist diff --git a/docs/user-guides/Pre_commit_hooks_reference.md b/docs/user-guides/Pre_commit_hooks_reference.md index 54d48c00..527c1e58 100644 --- a/docs/user-guides/Pre_commit_hooks_reference.md +++ b/docs/user-guides/Pre_commit_hooks_reference.md @@ -224,7 +224,7 @@ Missing platform: darwin_amd64 Option 1: Use the upgrade helper (recommended) ```bash -./scripts/terraform/upgrade-module.sh infrastructure/modules/s3-bucket +mise run terraform-upgrade-module -- infrastructure/modules/s3-bucket ``` Option 2: Manual regeneration @@ -322,7 +322,7 @@ updates: If the hook is skipped or fails silently, run manually: ```bash -bash scripts/generate-dependabot-config.sh .github/dependabot.yaml +mise run generate-dependabot-config -- .github/dependabot.yaml ``` To verify the configuration is valid: @@ -648,7 +648,7 @@ git commit --amend | --- | --- | | Terraform formatting | `terraform fmt -recursive infrastructure/modules/` | | Module docs out of sync | `pre-commit run terraform_docs --all-files` | -| Provider locks missing platforms | `./scripts/terraform/upgrade-module.sh infrastructure/modules/` | +| Provider locks missing platforms | `mise run terraform-upgrade-module -- infrastructure/modules/` | | Dependabot config out of date | Commit the regenerated `.github/dependabot.yaml` | | Shell script errors | Fix the issue; re-run `pre-commit run shellcheck` | | Trailing whitespace | `pre-commit run --all-files` (auto-fixed) | diff --git a/docs/user-guides/Scan_dependencies.md b/docs/user-guides/Scan_dependencies.md index 4145897e..9a5aa844 100644 --- a/docs/user-guides/Scan_dependencies.md +++ b/docs/user-guides/Scan_dependencies.md @@ -41,14 +41,14 @@ You can run and test the process locally on a developer's workstation using the SBOM generator ```shell -./scripts/reports/create-sbom-report.sh +mise run reports-create-sbom cat sbom-repository-report.json | jq ``` CVE scanner ```shell -./scripts/reports/scan-vulnerabilities.sh +mise run reports-scan-vulnerabilities cat vulnerabilities-repository-reportc.json | jq ``` diff --git a/docs/user-guides/Scan_secrets.md b/docs/user-guides/Scan_secrets.md index 1e3e1e10..25984b19 100644 --- a/docs/user-guides/Scan_secrets.md +++ b/docs/user-guides/Scan_secrets.md @@ -33,7 +33,7 @@ Scanning a repository for hard-coded secrets is a crucial security practice. "Ha You can execute and test the secret scanning across all commits locally on a developer's workstation using the following command ```shell -ALL_FILES=true ./scripts/githooks/scan-secrets.sh +ALL_FILES=true mise run githooks-scan-secrets ``` ## Removing sensitive data diff --git a/mise.toml b/mise.toml index 7706c2aa..e1ae41ef 100644 --- a/mise.toml +++ b/mise.toml @@ -22,3 +22,106 @@ make = "4.4.1" # default version on macos is too old # Go-based tools using backend syntax "go:github.com/hashicorp/terraform-config-inspect" = "latest" + +# Referenced tasks (used by current automation/documentation) + +[tasks.update-tool-versions] +description = "Update mise-managed tool versions, sync manifests, refresh README prerequisites, and regenerate lockfile" +run = "scripts/mise/update-tool-versions.sh" + +[tasks.generate-dependabot-config] +description = "Generate Dependabot configuration from infrastructure/modules" +run = "scripts/generate-dependabot-config.sh" + +[tasks.shellscript-linter] +description = "Run shell script lint wrapper" +run = "scripts/shellscript-linter.sh" + +[tasks.terraform-upgrade-module] +description = "Refresh Terraform lockfiles/docs for a module or all modules" +run = "scripts/terraform/upgrade-module.sh" + +[tasks.terraform-derive-module-constraints] +description = "Derive Terraform module constraints" +run = "scripts/terraform/derive-module-constraints.sh" + +[tasks.terraform-wrapper] +description = "Run repository Terraform wrapper" +run = "scripts/terraform/terraform.sh" + +[tasks.reports-create-lines-of-code] +description = "Generate lines-of-code report" +run = "scripts/reports/create-lines-of-code-report.sh" + +[tasks.reports-create-sbom] +description = "Generate software bill of materials report" +run = "scripts/reports/create-sbom-report.sh" + +[tasks.reports-perform-static-analysis] +description = "Run static analysis report generation" +run = "scripts/reports/perform-static-analysis.sh" + +[tasks.reports-scan-vulnerabilities] +description = "Scan generated SBOM for vulnerabilities" +run = "scripts/reports/scan-vulnerabilities.sh" + +[tasks.githooks-check-dependabot-config] +description = "Check and regenerate Dependabot configuration" +run = "scripts/githooks/check-dependabot-config.sh" + +[tasks.githooks-check-english-usage] +description = "Run English usage checks" +run = "scripts/githooks/check-english-usage.sh" + +[tasks.githooks-check-file-format] +description = "Run file format checks" +run = "scripts/githooks/check-file-format.sh" + +[tasks.githooks-check-markdown-format] +description = "Run Markdown format checks" +run = "scripts/githooks/check-markdown-format.sh" + +[tasks.githooks-check-terraform-format] +description = "Run Terraform format checks" +run = "scripts/githooks/check-terraform-format.sh" + +[tasks.githooks-generate-terraform-providers] +description = "Generate aliased Terraform provider stubs" +run = "scripts/githooks/generate-terraform-providers.sh" + +[tasks.githooks-scan-secrets] +description = "Scan for hardcoded secrets" +run = "scripts/githooks/scan-secrets.sh" + +[tasks.githooks-validate-conventional-commit] +description = "Validate Conventional Commit message format" +run = "scripts/githooks/validate-conventional-commit.sh" + +# Unreferenced tasks (starter/manual scripts; disabled by default) +# [tasks.docker-dgoss] +# description = "Run dgoss test harness" +# run = "scripts/docker/dgoss.sh" + +# [tasks.docker-dockerfile-linter] +# description = "Lint Dockerfiles via repository wrapper" +# run = "scripts/docker/dockerfile-linter.sh" + +# [tasks.docker-tests] +# description = "Run Docker script test suite" +# run = "scripts/docker/tests/docker.test.sh" + +# [tasks.tests-unit] +# description = "Run unit-style script tests" +# run = "scripts/tests/unit.sh" + +# [tasks.tests-style] +# description = "Run style-oriented script tests" +# run = "scripts/tests/style.sh" + +# [tasks.tests-release-config] +# description = "Run release configuration tests" +# run = "scripts/tests/release-config.sh" + +# [tasks.tests-release-updater] +# description = "Run release updater tests" +# run = "scripts/tests/release-updater.sh" diff --git a/tests/README.md b/tests/README.md index 5b6192ab..6535cf34 100644 --- a/tests/README.md +++ b/tests/README.md @@ -8,6 +8,7 @@ This repository includes comprehensive test coverage for new features and config - **Workflow security** — GitHub Actions and pre-commit hook pinning verification - **Tool version synchronization** — `.tool-versions` and `mise.toml` consistency - **Tool version upgrade automation** — Script and workflow logic for upgrading mise-managed tools +- **mise task surface policy** — Referenced tasks remain active and commented-out tasks stay unreferenced ## Running Tests @@ -93,6 +94,20 @@ bash tests/test-generate-dependabot-config.sh - ✓ Script output is idempotent (running twice produces identical output) - ✓ All discovered modules accounted for in configuration +#### mise Task Surface Policy Tests + +Enforces task-surface consistency between `mise.toml` and current automation/docs references. + +```bash +bash tests/test-mise-task-surface.sh +``` + +**Test Coverage:** + +- ✓ Every `mise run ` reference in workflows/actions/pre-commit/README/tests points to an active task in `mise.toml` +- ✓ Every active task in `mise.toml` is either referenced or explicitly allowed as maintained manual task +- ✓ No commented-out task in `mise.toml` is referenced by automation/docs/tests + ## Test Results All tests pass with the current configuration: @@ -102,7 +117,7 @@ All tests pass with the current configuration: ✓ Workflow Security Pinning: 15 tests passed ✓ Tool Version Upgrade Helper: 5+ tests passed ✓ Dependabot Configuration Generation: 9+ tests passed -✓ Total: 50+ test cases across 4 test suites +✓ Total: 70+ test cases across 5 test suites ``` ## Integration with CI/CD @@ -178,7 +193,7 @@ Test individual components manually: ```bash # Test conventional commit validator directly echo "feat(scope): test message" > /tmp/test-msg.txt -bash scripts/githooks/validate-conventional-commit.sh /tmp/test-msg.txt +mise run githooks-validate-conventional-commit -- /tmp/test-msg.txt echo "Exit code: $?" # 0 = pass, 1 = fail # Check action pinning diff --git a/tests/run-all-tests.sh b/tests/run-all-tests.sh index f67cbf0e..a8e2e112 100755 --- a/tests/run-all-tests.sh +++ b/tests/run-all-tests.sh @@ -99,6 +99,19 @@ else fi echo "" +# Test 6: mise Task Surface Policy +echo -e "${BLUE}Running: mise Task Surface Policy Tests${NC}" +echo "----------------------------------------------------------------------" +if bash tests/test-mise-task-surface.sh "${VERBOSE:-}" > /tmp/test-mise-task-surface.log 2>&1; then + cat /tmp/test-mise-task-surface.log + echo -e "${GREEN}✓ mise task surface policy tests passed${NC}" +else + cat /tmp/test-mise-task-surface.log + echo -e "${RED}✗ mise task surface policy tests failed${NC}" + TOTAL_FAILED=$((TOTAL_FAILED + 1)) +fi +echo "" + # Final summary echo "======================================================================" echo "Test Suite Summary" diff --git a/tests/test-mise-task-surface.sh b/tests/test-mise-task-surface.sh new file mode 100755 index 00000000..7bff221d --- /dev/null +++ b/tests/test-mise-task-surface.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash + +set -euo pipefail + +FAILED=0 +PASSED=0 + +RED='\033[0;31m' +GREEN='\033[0;32m' +NC='\033[0m' + +MiseFile="mise.toml" + +# Tasks intentionally kept active for developer/manual use. +# These are part of the maintained task surface even if not invoked in CI hooks. +ALLOWED_MANUAL_ACTIVE_TASKS=( + "terraform-derive-module-constraints" + "terraform-wrapper" +) + +assert_success() { + local ok="$1" + local label="$2" + + printf "%-70s ... " "$label" + if [[ "$ok" == "true" ]]; then + PASSED=$((PASSED + 1)) + printf "%b\n" "${GREEN}✓${NC}" + else + FAILED=$((FAILED + 1)) + printf "%b\n" "${RED}✗${NC}" + fi +} + +contains() { + local needle="$1" + shift + local item + for item in "$@"; do + if [[ "$item" == "$needle" ]]; then + return 0 + fi + done + return 1 +} + +echo "======================================================================" +echo "Testing mise task surface policy" +echo "======================================================================" +echo + +if [[ ! -f "$MiseFile" ]]; then + echo "Expected $MiseFile to exist" >&2 + exit 1 +fi + +mapfile -t active_tasks < <(rg '^\[tasks\.([^\]]+)\]$' "$MiseFile" -or '$1' | sort -u) +mapfile -t commented_tasks < <(rg '^# \[tasks\.([^\]]+)\]$' "$MiseFile" -or '$1' | sort -u) + +if [[ "${#active_tasks[@]}" -eq 0 ]]; then + echo "No active tasks were found in $MiseFile" >&2 + exit 1 +fi + +mapfile -t referenced_tasks < <( + rg -n 'mise run [A-Za-z0-9._-]+' \ + .github/workflows \ + .github/actions \ + .pre-commit-config.yaml \ + README.md \ + tests \ + | sed -E 's/.*mise run ([A-Za-z0-9._-]+).*/\1/' \ + | sort -u +) + +if [[ "${#referenced_tasks[@]}" -eq 0 ]]; then + echo "No referenced mise tasks were discovered. Check search inputs." >&2 + exit 1 +fi + +# 1) Every referenced task must be active. +for task in "${referenced_tasks[@]}"; do + if contains "$task" "${active_tasks[@]}"; then + assert_success true "Referenced task is active: $task" + else + assert_success false "Referenced task is active: $task" + fi +done + +# 2) Every active task must be referenced or explicitly allowed manual. +for task in "${active_tasks[@]}"; do + if contains "$task" "${referenced_tasks[@]}" || contains "$task" "${ALLOWED_MANUAL_ACTIVE_TASKS[@]}"; then + assert_success true "Active task is referenced/allowed: $task" + else + assert_success false "Active task is referenced/allowed: $task" + fi +done + +# 3) No commented-out task may be referenced by automation/docs/tests. +for task in "${commented_tasks[@]}"; do + if contains "$task" "${referenced_tasks[@]}"; then + assert_success false "Commented task is not referenced: $task" + else + assert_success true "Commented task is not referenced: $task" + fi +done + +echo +echo "======================================================================" +echo "Test Summary" +echo "======================================================================" +printf "Passed: %b\n" "${GREEN}${PASSED}${NC}" +printf "Failed: %b\n" "${RED}${FAILED}${NC}" +printf "Total: %d\n" $((PASSED + FAILED)) +echo + +if [[ "$FAILED" -eq 0 ]]; then + printf "%b\n" "${GREEN}✓ Task surface policy checks passed!${NC}" + exit 0 +else + printf "%b\n" "${RED}✗ Task surface policy checks failed!${NC}" + exit 1 +fi diff --git a/tests/test-module-upgrade.sh b/tests/test-module-upgrade.sh index 7accb482..ab508e6f 100755 --- a/tests/test-module-upgrade.sh +++ b/tests/test-module-upgrade.sh @@ -103,7 +103,7 @@ echo PATH="$bin_root:$PATH" bash "$repo_root/$SCRIPT" "$fixture_root/infrastructure/modules/example-one" assert_contains "terraform:-chdir=$relative_fixture_root/infrastructure/modules/example-one init -upgrade" "Single-module init uses -upgrade" -assert_contains "terraform:-chdir=$relative_fixture_root/infrastructure/modules/example-one providers lock -platform=linux_arm64 -platform=linux_amd64 -platform=darwin_amd64 -platform=windows_amd64" "Single-module providers lock runs with all target platforms" +assert_contains "terraform:-chdir=$relative_fixture_root/infrastructure/modules/example-one providers lock -platform=linux_arm64 -platform=linux_amd64 -platform=darwin_arm64 -platform=darwin_amd64 -platform=windows_amd64" "Single-module providers lock runs with all target platforms" assert_contains "pre-commit:run terraform_docs --files $relative_fixture_root/infrastructure/modules/example-one/README.md" "Single-module terraform_docs runs against README.md" printf '%s\n' '' > "$log_file" diff --git a/tests/test-tool-version-upgrade.sh b/tests/test-tool-version-upgrade.sh index a0c10e17..1694355c 100755 --- a/tests/test-tool-version-upgrade.sh +++ b/tests/test-tool-version-upgrade.sh @@ -33,8 +33,18 @@ lockfile = true [tools] terraform = "1.13.2" +tflint = "0.62.1" +terraform-docs = "0.24.0" +pre-commit = "4.6.0" python = "3.12.0" +vale = "3.6.0" +gitleaks = "8.30.1" +shellcheck = "0.11.0" +actionlint = "1.7.12" +jq = "1.8.1" +yq = "4.53.3" node = "20.11.0" +make = "4.4.1" "go:github.com/hashicorp/terraform-config-inspect" = "latest" EOF @@ -47,6 +57,15 @@ EOF cat > "$fixture_root/mise.lock" <<'EOF' # old lock +EOF + + cat > "$fixture_root/README.md" <<'EOF' +# Fixture README + + +| Tool | Version | Purpose | +| --- | --- | --- | + EOF } @@ -76,49 +95,15 @@ JSON ;; upgrade) if [[ "$*" == "upgrade --local --bump terraform" ]]; then - cat > "$repo_root/mise.toml" <<'TOML' -[settings] -lockfile = true - -[tools] -terraform = "1.13.3" -python = "3.12.0" -node = "20.11.0" -"go:github.com/hashicorp/terraform-config-inspect" = "latest" -TOML + perl -0pi -e 's/terraform = "1\.13\.2"/terraform = "1.13.3"/' "$repo_root/mise.toml" elif [[ "$*" == "upgrade --local --bump python" ]]; then - cat > "$repo_root/mise.toml" <<'TOML' -[settings] -lockfile = true - -[tools] -terraform = "1.13.2" -python = "3.13.0" -node = "20.11.0" -"go:github.com/hashicorp/terraform-config-inspect" = "latest" -TOML + perl -0pi -e 's/python = "3\.12\.0"/python = "3.13.0"/' "$repo_root/mise.toml" elif [[ "$*" == "upgrade --local --bump node" ]]; then - cat > "$repo_root/mise.toml" <<'TOML' -[settings] -lockfile = true - -[tools] -terraform = "1.13.2" -python = "3.12.0" -node = "21.1.0" -"go:github.com/hashicorp/terraform-config-inspect" = "latest" -TOML + perl -0pi -e 's/node = "20\.11\.0"/node = "21.1.0"/' "$repo_root/mise.toml" else - cat > "$repo_root/mise.toml" <<'TOML' -[settings] -lockfile = true - -[tools] -terraform = "1.13.3" -python = "3.13" -node = "21.1.0" -"go:github.com/hashicorp/terraform-config-inspect" = "latest" -TOML + perl -0pi -e 's/terraform = "1\.13\.2"/terraform = "1.13.3"/' "$repo_root/mise.toml" + perl -0pi -e 's/python = "3\.12\.0"/python = "3.13"/' "$repo_root/mise.toml" + perl -0pi -e 's/node = "20\.11\.0"/node = "21.1.0"/' "$repo_root/mise.toml" fi ;; lock) diff --git a/tests/test-workflow-security.sh b/tests/test-workflow-security.sh index 9564916a..26491ced 100755 --- a/tests/test-workflow-security.sh +++ b/tests/test-workflow-security.sh @@ -83,7 +83,7 @@ check_file_exists ".github/workflows/dependency-tools-mise-upgrade.yml" test_action_pinned ".github/workflows/dependency-tools-mise-upgrade.yml" "actions/checkout" test_action_pinned ".github/workflows/dependency-tools-mise-upgrade.yml" "jdx/mise-action" test_action_pinned ".github/workflows/dependency-tools-mise-upgrade.yml" "peter-evans/create-pull-request" -test_pattern_exists ".github/workflows/dependency-tools-mise-upgrade.yml" "scripts/mise/update-tool-versions.sh" "Shared tool-version update helper is used" +test_pattern_exists ".github/workflows/dependency-tools-mise-upgrade.yml" "mise run update-tool-versions" "Shared tool-version update helper task is used" echo "" # Check pre-commit configuration @@ -93,8 +93,8 @@ check_file_exists ".pre-commit-config.yaml" test_pattern_exists ".pre-commit-config.yaml" "rev:.*[0-9a-f]\\{40\\}" "Repos pinned to commit SHAs" test_pattern_exists ".pre-commit-config.yaml" "#.*v[0-9]" "Version comments for readability" test_pattern_exists ".pre-commit-config.yaml" "scripts/githooks/validate-conventional-commit.sh" "Local conventional commit validator" -test_pattern_exists ".pre-commit-config.yaml" "scripts/githooks/generate-terraform-providers.sh" "Local provider generator" -test_pattern_exists ".pre-commit-config.yaml" "scripts/shellscript-linter.sh" "Shellcheck hook uses wrapper script" +test_pattern_exists ".pre-commit-config.yaml" "mise run githooks-generate-terraform-providers" "Local provider generator uses mise task" +test_pattern_exists ".pre-commit-config.yaml" "mise run shellscript-linter" "Shellcheck hook uses wrapper task" test_pattern_exists "scripts/shellscript-linter.sh" "FORCE_USE_DOCKER" "Shellcheck wrapper supports Docker fallback override" echo "" From ab6213847a09305724f1c9889580ec79a3a28f0e Mon Sep 17 00:00:00 2001 From: Oliver Slater Date: Fri, 19 Jun 2026 13:44:57 +0100 Subject: [PATCH 2/3] refactor: migrate tests to bats framework and deprecate old scripts - Replaced test-conventional-commit.sh with test-conventional-commit.bats. - Replaced test-generate-dependabot-config.sh with test-generate-dependabot-config.bats. - Replaced test-mise-task-surface.sh with test-mise-task-surface.bats. - Replaced test-module-upgrade.sh with test-module-upgrade.bats. - Replaced test-tool-version-upgrade.sh with test-tool-version-upgrade.bats. - Replaced test-workflow-security.sh with test-workflow-security.bats. - Added shared assertion helpers in tests/test_helper/assertions.bash. - Updated tests to use new assertion methods and improved structure. --- .tool-versions | 19 +- mise.lock | 23 ++ mise.toml | 23 +- scripts/mise/update-tool-versions.sh | 61 +++- tests/README.md | 227 ++++++------- tests/run-all-tests.sh | 133 +------- tests/test-conventional-commit.bats | 177 +++++++++++ tests/test-conventional-commit.sh | 3 + tests/test-generate-dependabot-config.bats | 197 ++++++++++++ tests/test-generate-dependabot-config.sh | 2 + tests/test-mise-task-surface.bats | 112 +++++++ tests/test-mise-task-surface.sh | 9 +- tests/test-module-upgrade.bats | 103 ++++++ tests/test-module-upgrade.sh | 2 + tests/test-tool-version-upgrade.bats | 352 +++++++++++++++++++++ tests/test-tool-version-upgrade.sh | 36 ++- tests/test-workflow-security.bats | 112 +++++++ tests/test-workflow-security.sh | 3 + tests/test_helper/assertions.bash | 87 +++++ 19 files changed, 1403 insertions(+), 278 deletions(-) mode change 100755 => 100644 tests/run-all-tests.sh create mode 100644 tests/test-conventional-commit.bats create mode 100644 tests/test-generate-dependabot-config.bats create mode 100644 tests/test-mise-task-surface.bats create mode 100644 tests/test-module-upgrade.bats create mode 100644 tests/test-tool-version-upgrade.bats create mode 100644 tests/test-workflow-security.bats create mode 100644 tests/test_helper/assertions.bash diff --git a/.tool-versions b/.tool-versions index bc9593b0..4494437e 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,20 +1,21 @@ # This file is for you! Please, updated to the versions agreed by your team. -terraform 1.13.5 -tflint 0.62.1 -terraform-docs 0.24.0 +actionlint 1.7.12 +bats 1.13.0 +gitleaks 8.30.1 go 1.26.4 go:github.com/hashicorp/terraform-config-inspect latest +jq 1.8.1 +make 4.4.1 +nodejs 24.16.0 pre-commit 4.6.0 python 3.12 -vale 3.6.0 -gitleaks 8.30.1 shellcheck 0.11.0 -actionlint 1.7.12 -jq 1.8.1 +terraform 1.13.5 +terraform-docs 0.24.0 +tflint 0.62.1 +vale 3.6.0 yq 4.53.3 -nodejs 24.16.0 -make 4.4.1 # ============================================================================== # The section below is reserved for Docker image versions. diff --git a/mise.lock b/mise.lock index a9e4a34c..1c4936a6 100644 --- a/mise.lock +++ b/mise.lock @@ -83,6 +83,29 @@ checksum = "sha256:6e7241b51e6817ea6a047693d8e6fed13b31819c9a0dd6c5a726e1592d22f url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_windows_amd64.zip" provenance = "github-attestations" +[[tools.bats]] +version = "1.13.0" +backend = "aqua:bats-core/bats-core" + +[tools.bats."platforms.linux-arm64"] +url = "https://github.com/bats-core/bats-core/archive/refs/tags/v1.13.0.tar.gz" + +[tools.bats."platforms.linux-arm64-musl"] +url = "https://github.com/bats-core/bats-core/archive/refs/tags/v1.13.0.tar.gz" + +[tools.bats."platforms.linux-x64"] +url = "https://github.com/bats-core/bats-core/archive/refs/tags/v1.13.0.tar.gz" + +[tools.bats."platforms.linux-x64-musl"] +url = "https://github.com/bats-core/bats-core/archive/refs/tags/v1.13.0.tar.gz" + +[tools.bats."platforms.macos-arm64"] +checksum = "blake3:d34af9fd0f9101d5309b2de31f3d1699e56a574d7d4b5e6380580a9e758a9744" +url = "https://github.com/bats-core/bats-core/archive/refs/tags/v1.13.0.tar.gz" + +[tools.bats."platforms.macos-x64"] +url = "https://github.com/bats-core/bats-core/archive/refs/tags/v1.13.0.tar.gz" + [[tools.gitleaks]] version = "8.30.1" backend = "aqua:gitleaks/gitleaks" diff --git a/mise.toml b/mise.toml index e1ae41ef..dc2d2487 100644 --- a/mise.toml +++ b/mise.toml @@ -5,22 +5,23 @@ auto_install = true idiomatic_version_file_enable_tools = ["node", "python", "terraform"] [tools] -terraform = "1.13.5" -tflint = "0.62.1" -terraform-docs = "0.24.0" +actionlint = "1.7.12" +bats = "1.13.0" +gitleaks = "8.30.1" +go = "1.26.4" # needed for installing go-based tools from source +jq = "1.8.1" +make = "4.4.1" # default version on macos is too old +nodejs = "24.16.0" pre-commit = "4.6.0" python = "3.12" -vale = "3.6.0" -gitleaks = "8.30.1" shellcheck = "0.11.0" -actionlint = "1.7.12" -jq = "1.8.1" +terraform = "1.13.5" +terraform-docs = "0.24.0" +tflint = "0.62.1" +vale = "3.6.0" yq = "4.53.3" -nodejs = "24.16.0" -go = "1.26.4" # needed for installing go-based tools from source -make = "4.4.1" # default version on macos is too old -# Go-based tools using backend syntax +# Go-based tools (backend syntax requires quoting) "go:github.com/hashicorp/terraform-config-inspect" = "latest" # Referenced tasks (used by current automation/documentation) diff --git a/scripts/mise/update-tool-versions.sh b/scripts/mise/update-tool-versions.sh index 87e115c2..448e44e4 100755 --- a/scripts/mise/update-tool-versions.sh +++ b/scripts/mise/update-tool-versions.sh @@ -248,41 +248,70 @@ sync_readme_prerequisites_from_toml() { local readme_file="README.md" local tmp_file local line + local in_block=false if [[ ! -f "$readme_file" ]]; then echo "Expected file README.md was not found" >&2 exit 1 fi + # Tool metadata registry: tool_key -> "display_name|url|purpose|version_prefix" + # Only tools listed here appear in the generated prerequisites table. + # Add new entries when introducing a tool that developers must install. + declare -A tool_metadata=( + [actionlint]="actionlint|https://github.com/rhysd/actionlint|GitHub Actions linter|" + [bats]="bats-core|https://github.com/bats-core/bats-core|Bash test framework|" + [gitleaks]="Gitleaks|https://github.com/gitleaks/gitleaks|Secret scanning|" + [go:github.com/hashicorp/terraform-config-inspect]="terraform-config-inspect|https://github.com/hashicorp/terraform-config-inspect|Generate aliased providers for validation|" + [jq]="jq|https://jqlang.github.io/jq/|JSON processor|" + [make]="GNU make|https://www.gnu.org/software/make/|Task runner|>= " + [pre-commit]="pre-commit|https://pre-commit.com/|Git hook framework|" + [shellcheck]="shellcheck|https://www.shellcheck.net/|Shell script linter|" + [terraform]="Terraform|https://www.terraform.io/|Infrastructure as code|>= " + [terraform-docs]="terraform-docs|https://terraform-docs.io/|Auto-generate module documentation|" + [tflint]="tflint|https://github.com/terraform-linters/tflint|Terraform linter|" + [vale]="Vale|https://vale.sh/|English prose linter|" + [yq]="yq|https://github.com/mikefarah/yq|YAML processor (Go implementation)|" + ) + + # Build the table rows for tools present in both metadata and toml_versions + local rows="" + local key display_name url purpose version_prefix version + for key in "${!tool_metadata[@]}"; do + if [[ -z "${toml_versions[$key]:-}" ]]; then + continue + fi + IFS='|' read -r display_name url purpose version_prefix <<< "${tool_metadata[$key]}" + version="${version_prefix}${toml_versions[$key]}" + rows+="| [$display_name]($url) | $version | $purpose |"$'\n' + done + + # Sort rows alphabetically by display name (first column) + rows="$(printf '%s' "$rows" | sort -t'[' -k2,2 -f)" + tmp_file="$(mktemp)" while IFS= read -r line; do if [[ "$line" == "" ]]; then + in_block=true printf '%s\n' "$line" >> "$tmp_file" - cat <> "$tmp_file" -| Tool | Version | Purpose | -| --- | --- | --- | -| [Terraform](https://www.terraform.io/) | >= ${toml_versions[terraform]} | Infrastructure as code | -| [tflint](https://github.com/terraform-linters/tflint) | ${toml_versions[tflint]} | Terraform linter | -| [terraform-docs](https://terraform-docs.io/) | ${toml_versions[terraform-docs]} | Auto-generate module documentation | -| [terraform-config-inspect](https://github.com/hashicorp/terraform-config-inspect) | ${toml_versions[go:github.com/hashicorp/terraform-config-inspect]} | Generate aliased providers for validation | -| [pre-commit](https://pre-commit.com/) | ${toml_versions[pre-commit]} | Git hook framework | -| [Vale](https://vale.sh/) | ${toml_versions[vale]} | English prose linter | -| [Gitleaks](https://github.com/gitleaks/gitleaks) | ${toml_versions[gitleaks]} | Secret scanning | -| [yq](https://github.com/mikefarah/yq) | ${toml_versions[yq]} | YAML processor (Go implementation) | -| [jq](https://jqlang.github.io/jq/) | ${toml_versions[jq]} | JSON processor | -| [actionlint](https://github.com/rhysd/actionlint) | ${toml_versions[actionlint]} | GitHub Actions linter | -| [shellcheck](https://www.shellcheck.net/) | ${toml_versions[shellcheck]} | Shell script linter | -| [GNU make](https://www.gnu.org/software/make/) | >= ${toml_versions[make]} | Task runner | -EOF + printf '%s\n' "| Tool | Version | Purpose |" >> "$tmp_file" + printf '%s\n' "| --- | --- | --- |" >> "$tmp_file" + printf '%s\n' "$rows" >> "$tmp_file" continue fi if [[ "$line" == "" ]]; then + in_block=false printf '%s\n' "$line" >> "$tmp_file" continue fi + # Skip old content between the markers + if [[ "$in_block" == "true" ]]; then + continue + fi + printf '%s\n' "$line" >> "$tmp_file" done < "$readme_file" diff --git a/tests/README.md b/tests/README.md index 6535cf34..f0db53d4 100644 --- a/tests/README.md +++ b/tests/README.md @@ -2,43 +2,62 @@ ## Overview -This repository includes comprehensive test coverage for new features and configurations introduced on the `feature/BCSS-99999-fixup-workflows-actions-precommit` branch: +This repository uses [bats-core](https://github.com/bats-core/bats-core) (Bash Automated Testing System) for all shell script tests. Tests are located in `tests/*.bats` with shared assertion helpers in `tests/test_helper/`. + +Test coverage includes: - **Conventional commit validation** — Native bash implementation replacing external dependency - **Workflow security** — GitHub Actions and pre-commit hook pinning verification - **Tool version synchronization** — `.tool-versions` and `mise.toml` consistency - **Tool version upgrade automation** — Script and workflow logic for upgrading mise-managed tools +- **Dependabot config generation** — Automatic Terraform module discovery - **mise task surface policy** — Referenced tasks remain active and commented-out tasks stay unreferenced +## Prerequisites + +- `bats` 1.13.0+ (managed via mise; run `mise install` to set up) + ## Running Tests ### Run All Tests ```bash -bash tests/run-all-tests.sh -bash tests/run-all-tests.sh verbose # Show message examples +bash tests/run-all-tests.sh # Pretty output (default for terminals) +bash tests/run-all-tests.sh --tap # TAP output (for CI) +bats tests/*.bats # Direct bats invocation ``` ### Run Individual Test Suites -#### Conventional Commit Validation Tests +```bash +bats tests/test-conventional-commit.bats +bats tests/test-workflow-security.bats +bats tests/test-module-upgrade.bats +bats tests/test-tool-version-upgrade.bats +bats tests/test-generate-dependabot-config.bats +bats tests/test-mise-task-surface.bats +``` -Tests the native bash validation script that replaces the external `compilerla/conventional-pre-commit` dependency. +### Run a Single Test by Name ```bash -bash tests/test-conventional-commit.sh -bash tests/test-conventional-commit.sh verbose # Show detailed output +bats tests/test-conventional-commit.bats --filter "feat with scope" ``` -**Test Coverage:** +## Test Suites + +### Conventional Commit Validation + +Tests the native bash validation script that replaces the external `compilerla/conventional-pre-commit` dependency. -- ✓ Valid commits with scope: `feat(scope): description` -- ✓ Valid commits without scope: `feat: description` -- ✓ Invalid format detection (missing colons, empty descriptions) -- ✓ Invalid type detection (allowed types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`) -- 22 total test cases +**Coverage:** -#### Workflow Security Pinning Tests +- Valid commits with scope: `feat(scope): description` +- Valid commits without scope: `feat: description` +- Invalid format detection (missing colons, empty descriptions) +- Invalid type detection (allowed types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`) + +### Workflow Security Pinning Validates that GitHub Actions and pre-commit hooks use immutable references (commit SHAs) with version comments. @@ -47,143 +66,129 @@ bash tests/test-workflow-security.sh bash tests/test-workflow-security.sh verbose # Show detailed output ``` -**Test Coverage:** - -- ✓ GitHub Actions pinned to commit SHAs (actions/checkout, jdx/mise-action, actions/cache) -- ✓ Environment variables configured (AWS_DEFAULT_REGION, TF_PLUGIN_CACHE_DIR) -- ✓ shellcheck Docker fallback capability retained (FORCE_USE_DOCKER support in wrapper) -- ✓ Pre-commit repos pinned to commit SHAs -- ✓ Version comments present for human readability -- ✓ Local custom hooks exist and are executable -- ✓ Tool version files synchronized -- 15 total test cases +**Coverage:** -#### Tool Version Upgrade Helper Tests - -Tests the shared bash helper used both locally and in CI to update `mise.toml`, sync `.tool-versions`, and regenerate `mise.lock`. +- GitHub Actions pinned to commit SHAs with version comments +- Environment variables configured (AWS_DEFAULT_REGION, TF_PLUGIN_CACHE_DIR) +- Pre-commit repos pinned to commit SHAs +- Local custom hooks exist and are executable +- Tool version files synchronised between `.tool-versions` and `mise.toml` -```bash -bash tests/test-tool-version-upgrade.sh -``` +### Terraform Module Upgrade Helper -**Test Coverage:** +Tests the upgrade helper that refreshes provider locks and documentation. -- ✓ Calls `mise install`, `mise upgrade --local --bump`, and `mise lock` -- ✓ Synchronizes `.tool-versions` values from upgraded `mise.toml` -- ✓ Preserves alias-style tool keys (for example `go:...`) -- ✓ Supports `--dry-run` without changing files -- ✓ Supports all `--upgrade-level` modes (`patch`, `minor`, `major`, and `all`) +**Coverage:** -#### Dependabot Configuration Generation Tests +- Single-module init with `-upgrade` flag +- Provider lock across all target platforms +- `terraform_docs` invocation against README +- `update-all` mode processes modules sequentially -Tests the Dependabot YAML configuration generator that maintains `.github/dependabot.yaml` by automatically discovering all Terraform modules. +### Tool Version Upgrade Helper -```bash -bash tests/test-generate-dependabot-config.sh -``` +Tests the shared bash helper used both locally and in CI to update `mise.toml`, sync `.tool-versions`, and regenerate `mise.lock`. -**Test Coverage:** +**Coverage:** -- ✓ Script exists and is executable -- ✓ Configuration generation succeeds -- ✓ Generated YAML is valid and parseable -- ✓ All required ecosystem entries preserved (docker, GitHub Actions, npm, pip) -- ✓ All Terraform modules discovered and added (34 modules in infrastructure/modules/) -- ✓ `.terraform/` cache directories excluded from configuration -- ✓ Weekly update schedule configured for all entries -- ✓ Script output is idempotent (running twice produces identical output) -- ✓ All discovered modules accounted for in configuration +- Calls `mise install`, `mise upgrade --local --bump`, and `mise lock` +- Synchronises `.tool-versions` values from upgraded `mise.toml` +- Preserves alias-style tool keys (for example `go:...`) +- Supports `--dry-run` without changing files +- Supports all `--upgrade-level` modes (`patch`, `minor`, `major`, and `all`) -#### mise Task Surface Policy Tests +### Dependabot Configuration Generation -Enforces task-surface consistency between `mise.toml` and current automation/docs references. +Tests the Dependabot YAML configuration generator that maintains `.github/dependabot.yaml`. -```bash -bash tests/test-mise-task-surface.sh -``` +**Coverage:** -**Test Coverage:** +- Script exists and is executable +- Generated YAML is valid and parseable +- All required ecosystem entries preserved (Docker, GitHub Actions) +- All Terraform modules discovered and included +- `.terraform/` cache directories excluded +- Script output is idempotent +- Template customisations outside markers are preserved -- ✓ Every `mise run ` reference in workflows/actions/pre-commit/README/tests points to an active task in `mise.toml` -- ✓ Every active task in `mise.toml` is either referenced or explicitly allowed as maintained manual task -- ✓ No commented-out task in `mise.toml` is referenced by automation/docs/tests +### mise Task Surface Policy -## Test Results +Enforces task-surface consistency between `mise.toml` and current automation/docs references. -All tests pass with the current configuration: +**Coverage:** -```text -✓ Conventional Commit Validation: 22 tests passed -✓ Workflow Security Pinning: 15 tests passed -✓ Tool Version Upgrade Helper: 5+ tests passed -✓ Dependabot Configuration Generation: 9+ tests passed -✓ Total: 70+ test cases across 5 test suites -``` +- Every `mise run ` reference points to an active task +- Every active task is either referenced or explicitly allowed as a maintained manual task +- No commented-out task is referenced by automation/docs/tests ## Integration with CI/CD -Tests are designed to run locally and in CI/CD pipelines: +Tests produce TAP-compliant output for CI integration: ```yaml # Example GitHub Actions step - name: Run test suite - run: bash tests/run-all-tests.sh + run: bash tests/run-all-tests.sh --tap ``` ## Adding New Tests -To add new tests: - -1. Create a new test file in `tests/` directory (e.g., `tests/test-feature-name.sh`) -2. Make it executable: `chmod +x tests/test-feature-name.sh` -3. Add a call to the test runner in `tests/run-all-tests.sh` +1. Create a new `.bats` file in `tests/` (e.g., `tests/test-feature-name.bats`) +2. Load shared helpers: `load test_helper/assertions` +3. Tests are automatically picked up by `run-all-tests.sh` -### Test Script Template +### Test File Template ```bash -#!/usr/bin/env bash +#!/usr/bin/env bats # Test suite for feature-name -# Usage: bash tests/test-feature-name.sh - -FAILED=0 -PASSED=0 - -# Test helper -test_case() { - local description="$1" - local command="$2" - printf "Testing: %-50s ... " "$description" - - if eval "$command" >/dev/null 2>&1; then - PASSED=$((PASSED + 1)) - echo "✓" - else - FAILED=$((FAILED + 1)) - echo "✗" - fi + +load test_helper/assertions + +setup_file() { + # Expensive one-time setup (fixtures, mock binaries) + export REPO_ROOT + REPO_ROOT="$(git rev-parse --show-toplevel)" } -# Run tests -test_case "Description" "command" +teardown_file() { + # Clean up fixtures + : +} + +@test "description of expected behaviour" { + run some_command + [ "$status" -eq 0 ] + assert_contains "$output" "expected" +} -# Summary -if [ $FAILED -eq 0 ]; then - exit 0 -else - exit 1 -fi +@test "file contains expected content" { + assert_file_contains "path/to/file" "expected string" +} ``` -## Debugging Test Failures +### Available Assertions + +Defined in `tests/test_helper/assertions.bash`: -### Verbose Output +| Function | Purpose | +| --- | --- | +| `assert_file_contains ` | File contains fixed string | +| `assert_file_not_contains ` | File does not contain string | +| `assert_file_exists ` | File exists | +| `assert_file_matches ` | File matches regex pattern | +| `assert_line_order ` | First string appears before second | +| `assert_contains ` | String contains substring | +| `assert_not_contains ` | String does not contain substring | -Run tests with verbose flag to see detailed information: +## Debugging Test Failures ```bash -bash tests/test-conventional-commit.sh verbose -bash tests/test-workflow-security.sh verbose -bash tests/run-all-tests.sh verbose +# Run with verbose output +bats tests/test-conventional-commit.bats --trace + +# Run a specific failing test +bats tests/test-workflow-security.bats --filter "terraform version" ``` ### Manual Testing @@ -207,4 +212,4 @@ grep "rev:" .pre-commit-config.yaml - [Run Git hooks on commit](../docs/user-guides/Run_Git_hooks_on_commit.md) - [Test GitHub Actions locally](../docs/user-guides/Test_GitHub_Actions_locally.md) -- [Pre-commit configuration](.pre-commit-config.yaml) +- [Pre-commit configuration](../.pre-commit-config.yaml) diff --git a/tests/run-all-tests.sh b/tests/run-all-tests.sh old mode 100755 new mode 100644 index a8e2e112..97696c03 --- a/tests/run-all-tests.sh +++ b/tests/run-all-tests.sh @@ -1,134 +1,35 @@ #!/usr/bin/env bash -# Run all tests for screening-terraform-modules-aws -# -# Tests include: -# - Conventional commit validator functionality -# - GitHub Actions workflow security (action pinning, env vars) -# - Terraform module upgrade helper behaviour -# - Pre-commit configuration consistency -# - Tool version file synchronization -# - Tool version upgrade automation helper +# Run all bats test suites for screening-terraform-modules-aws. # # Usage: -# bash tests/run-all-tests.sh -# bash tests/run-all-tests.sh verbose +# bash tests/run-all-tests.sh # Standard TAP output +# bash tests/run-all-tests.sh --pretty # Pretty-printed output (if available) +# +# Prerequisites: +# bats-core (managed via mise; see .tool-versions) set -u -VERBOSE="${1:-}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" -TOTAL_FAILED=0 -# Colors -RED='\033[0;31m' -GREEN='\033[0;32m' -BLUE='\033[0;34m' -NC='\033[0m' +cd "$REPO_ROOT" || exit 1 -cd "$REPO_ROOT" || exit +if ! command -v bats &>/dev/null; then + echo "ERROR: bats-core is not installed. Run 'mise install' to set up tools." >&2 + exit 1 +fi -echo "" echo "======================================================================" -echo "Running Comprehensive Test Suite" +echo "Running bats test suites" echo "======================================================================" echo "" -# Test 1: Conventional Commit Validator -echo -e "${BLUE}Running: Conventional Commit Validator Tests${NC}" -echo "----------------------------------------------------------------------" -if bash tests/test-conventional-commit.sh "${VERBOSE:-}" > /tmp/test-conventional.log 2>&1; then - cat /tmp/test-conventional.log - echo -e "${GREEN}✓ Conventional commit tests passed${NC}" -else - cat /tmp/test-conventional.log - echo -e "${RED}✗ Conventional commit tests failed${NC}" - TOTAL_FAILED=$((TOTAL_FAILED + 1)) -fi -echo "" +bats_args=() -# Test 2: Workflow Security -echo -e "${BLUE}Running: Workflow Security Pinning Tests${NC}" -echo "----------------------------------------------------------------------" -if bash tests/test-workflow-security.sh "${VERBOSE:-}" > /tmp/test-workflow.log 2>&1; then - cat /tmp/test-workflow.log - echo -e "${GREEN}✓ Workflow security tests passed${NC}" -else - cat /tmp/test-workflow.log - echo -e "${RED}✗ Workflow security tests failed${NC}" - TOTAL_FAILED=$((TOTAL_FAILED + 1)) +# Use pretty formatter if available and terminal is interactive +if [[ "${1:-}" == "--pretty" ]] || [[ -t 1 && "${1:-}" != "--tap" ]]; then + bats_args+=(--formatter pretty) fi -echo "" - -# Test 3: Terraform Module Upgrade Helper -echo -e "${BLUE}Running: Terraform Module Upgrade Helper Tests${NC}" -echo "----------------------------------------------------------------------" -if bash tests/test-module-upgrade.sh "${VERBOSE:-}" > /tmp/test-module-upgrade.log 2>&1; then - cat /tmp/test-module-upgrade.log - echo -e "${GREEN}✓ Terraform module upgrade helper tests passed${NC}" -else - cat /tmp/test-module-upgrade.log - echo -e "${RED}✗ Terraform module upgrade helper tests failed${NC}" - TOTAL_FAILED=$((TOTAL_FAILED + 1)) -fi -echo "" -# Test 4: Tool Version Upgrade Helper -echo -e "${BLUE}Running: Tool Version Upgrade Helper Tests${NC}" -echo "----------------------------------------------------------------------" -if bash tests/test-tool-version-upgrade.sh "${VERBOSE:-}" > /tmp/test-tool-version-upgrade.log 2>&1; then - cat /tmp/test-tool-version-upgrade.log - echo -e "${GREEN}✓ Tool version upgrade helper tests passed${NC}" -else - cat /tmp/test-tool-version-upgrade.log - echo -e "${RED}✗ Tool version upgrade helper tests failed${NC}" - TOTAL_FAILED=$((TOTAL_FAILED + 1)) -fi -echo "" - -# Test 5: Dependabot Configuration Generation -echo -e "${BLUE}Running: Dependabot Configuration Generation Tests${NC}" -echo "----------------------------------------------------------------------" -if bash tests/test-generate-dependabot-config.sh "${VERBOSE:-}" > /tmp/test-dependabot-config.log 2>&1; then - cat /tmp/test-dependabot-config.log - echo -e "${GREEN}✓ Dependabot config generation tests passed${NC}" -else - cat /tmp/test-dependabot-config.log - echo -e "${RED}✗ Dependabot config generation tests failed${NC}" - TOTAL_FAILED=$((TOTAL_FAILED + 1)) -fi -echo "" - -# Test 6: mise Task Surface Policy -echo -e "${BLUE}Running: mise Task Surface Policy Tests${NC}" -echo "----------------------------------------------------------------------" -if bash tests/test-mise-task-surface.sh "${VERBOSE:-}" > /tmp/test-mise-task-surface.log 2>&1; then - cat /tmp/test-mise-task-surface.log - echo -e "${GREEN}✓ mise task surface policy tests passed${NC}" -else - cat /tmp/test-mise-task-surface.log - echo -e "${RED}✗ mise task surface policy tests failed${NC}" - TOTAL_FAILED=$((TOTAL_FAILED + 1)) -fi -echo "" - -# Final summary -echo "======================================================================" -echo "Test Suite Summary" -echo "======================================================================" - -if [ $TOTAL_FAILED -eq 0 ]; then - echo -e "${GREEN}✓ All test suites passed!${NC}" - echo "" - echo "Ready for commit and PR:" - echo " - Conventional commits validated with native bash hook" - echo " - GitHub Actions pinned to immutable commit SHAs" - echo " - Terraform module upgrade helper verified" - echo " - Pre-commit configuration verified for consistency" - echo " - Tool versions synchronized across .tool-versions and mise.toml" - echo " - Tool version upgrade helper verified" - exit 0 -else - echo -e "${RED}✗ $TOTAL_FAILED test suite(s) failed${NC}" - exit 1 -fi +exec bats "${bats_args[@]}" tests/*.bats diff --git a/tests/test-conventional-commit.bats b/tests/test-conventional-commit.bats new file mode 100644 index 00000000..a8758d08 --- /dev/null +++ b/tests/test-conventional-commit.bats @@ -0,0 +1,177 @@ +#!/usr/bin/env bats +# Test suite for scripts/githooks/validate-conventional-commit.sh +# +# Tests the bash-based conventional commit message validator. + +setup() { + VALIDATOR="scripts/githooks/validate-conventional-commit.sh" + COMMIT_MSG_FILE="$(mktemp)" +} + +teardown() { + rm -f "$COMMIT_MSG_FILE" +} + +# --- Valid commits with scope --- + +@test "valid: feat with scope" { + echo "feat(pre-commit): replace external hook with bash validator" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +@test "valid: fix with scope" { + echo "fix(tools): consolidate mise.toml and .tool-versions" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +@test "valid: docs with scope" { + echo "docs(readme): update installation instructions" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +@test "valid: docs with dotted scope" { + echo "docs(README.md): update example using jdx/mise-action version pinning" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +@test "valid: feat with breaking change marker" { + echo "feat!: remove legacy validator" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +@test "valid: feat with scope and breaking marker" { + echo "feat(validators)!: replace external validator" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +@test "valid: style with scope" { + echo "style(formatting): apply prettier to all files" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +@test "valid: refactor with scope" { + echo "refactor(workflows): simplify GitHub Actions configuration" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +@test "valid: perf with scope" { + echo "perf(ci): optimize terraform plugin caching" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +@test "valid: test with scope" { + echo "test(validators): add comprehensive test suite" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +@test "valid: build with scope" { + echo "build(deps): update terraform to 1.13.2" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +@test "valid: ci with scope" { + echo "ci(pre-commit): update hook configuration" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +@test "valid: chore with scope" { + echo "chore(deps): bump mise to 2024.6.0" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +@test "valid: revert with scope" { + echo "revert(pre-commit): revert breaking change to hook config" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +# --- Valid commits without scope --- + +@test "valid: feat without scope" { + echo "feat: add support for custom tool versions" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +@test "valid: fix without scope" { + echo "fix: resolve missing shellcheck dependency" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +@test "valid: docs without scope" { + echo "docs: clarify mise.toml requirements" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -eq 0 ] +} + +# --- Invalid commits: wrong format --- + +@test "invalid: no type prefix" { + echo "invalid message" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -ne 0 ] +} + +@test "invalid: missing colon after type" { + echo "feat add new feature" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -ne 0 ] +} + +@test "invalid: empty description" { + echo "feat: " > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -ne 0 ] +} + +@test "invalid: missing colon with scope" { + echo "feat(scope) missing colon" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -ne 0 ] +} + +@test "invalid: scope with spaces" { + echo "docs(read me): invalid spaced scope" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -ne 0 ] +} + +# --- Invalid commits: wrong type --- + +@test "invalid type: feature" { + echo "feature(scope): add new feature" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -ne 0 ] +} + +@test "invalid type: bug" { + echo "bug(scope): fix something" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -ne 0 ] +} + +@test "invalid type: hotfix" { + echo "hotfix(scope): urgent fix" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -ne 0 ] +} + +@test "invalid type: FEAT (uppercase)" { + echo "FEAT(scope): uppercase type" > "$COMMIT_MSG_FILE" + run bash "$VALIDATOR" "$COMMIT_MSG_FILE" + [ "$status" -ne 0 ] +} diff --git a/tests/test-conventional-commit.sh b/tests/test-conventional-commit.sh index 588c352d..7424ad70 100755 --- a/tests/test-conventional-commit.sh +++ b/tests/test-conventional-commit.sh @@ -1,4 +1,7 @@ #!/usr/bin/env bash +# DEPRECATED: This test file has been replaced by test-conventional-commit.bats +# which uses the bats-core test framework. Run: bats tests/test-conventional-commit.bats +# # Test suite for scripts/githooks/validate-conventional-commit.sh # # Tests the bash-based conventional commit message validator. diff --git a/tests/test-generate-dependabot-config.bats b/tests/test-generate-dependabot-config.bats new file mode 100644 index 00000000..d61f7bef --- /dev/null +++ b/tests/test-generate-dependabot-config.bats @@ -0,0 +1,197 @@ +#!/usr/bin/env bats +# Test suite for scripts/generate-dependabot-config.sh +# +# Tests the Dependabot configuration generation script. + +load test_helper/assertions + +setup_file() { + export REPO_ROOT + REPO_ROOT="$(git rev-parse --show-toplevel)" + export SCRIPT="scripts/generate-dependabot-config.sh" + + mkdir -p "$REPO_ROOT/tmp" + export FIXTURE_ROOT + FIXTURE_ROOT="$(mktemp -d "$REPO_ROOT/tmp/generate-dependabot-config.XXXXXX")" + + # Generate the config once for all tests to inspect + export OUTPUT_FILE="$FIXTURE_ROOT/dependabot.yaml" + bash "$REPO_ROOT/$SCRIPT" "$OUTPUT_FILE" > /dev/null 2>&1 + export CONFIG + CONFIG="$(cat "$OUTPUT_FILE")" +} + +teardown_file() { + rm -rf "$FIXTURE_ROOT" +} + +# --- Script availability --- + +@test "script exists" { + assert_file_exists "$REPO_ROOT/$SCRIPT" +} + +@test "script is executable" { + [ -x "$REPO_ROOT/$SCRIPT" ] +} + +# --- Config generation --- + +@test "script executes successfully" { + run bash "$REPO_ROOT/$SCRIPT" "$FIXTURE_ROOT/dependabot-rerun.yaml" + [ "$status" -eq 0 ] +} + +@test "output file is created" { + assert_file_exists "$OUTPUT_FILE" +} + +# --- YAML structure --- + +@test "generated YAML is valid" { + if command -v python3 &>/dev/null && python3 -c "import yaml" 2>/dev/null; then + python3 -c "import yaml; yaml.safe_load(open('$OUTPUT_FILE'))" + elif command -v yq &>/dev/null; then + yq eval '.' "$OUTPUT_FILE" > /dev/null 2>&1 + else + skip "No YAML validator available (need python3+pyyaml or yq)" + fi +} + +# --- Required sections --- + +@test "version field present" { + assert_contains "$CONFIG" "version: 2" +} + +@test "updates section present" { + assert_contains "$CONFIG" "updates:" +} + +@test "GitHub Actions ecosystem preserved" { + assert_contains "$CONFIG" 'package-ecosystem: "github-actions"' +} + +@test "Docker ecosystem preserved" { + assert_contains "$CONFIG" 'package-ecosystem: "docker"' +} + +@test "custom Docker directory preserved" { + assert_contains "$CONFIG" '- "/scripts/docker"' +} + +@test "custom Docker test directory preserved" { + assert_contains "$CONFIG" '- "/scripts/docker/tests"' +} + +@test "custom cooldown setting preserved" { + assert_contains "$CONFIG" "cooldown:" +} + +@test "custom PR limit preserved" { + assert_contains "$CONFIG" "open-pull-requests-limit: 10" +} + +@test "start marker preserved" { + assert_contains "$CONFIG" "# BEGIN_AUTOGENERATED_TERRAFORM_UPDATES" +} + +@test "end marker preserved" { + assert_contains "$CONFIG" "# END_AUTOGENERATED_TERRAFORM_UPDATES" +} + +# --- Terraform module entries --- + +@test "single Terraform update entry exists" { + local count + count=$(echo "$CONFIG" | grep -c 'package-ecosystem: "terraform"' || echo 0) + [ "$count" -eq 1 ] +} + +@test "Terraform uses directories directive" { + assert_contains "$CONFIG" "directories:" +} + +@test "known module: s3-bucket included" { + assert_contains "$CONFIG" "infrastructure/modules/s3-bucket" +} + +@test "known module: iam included" { + assert_contains "$CONFIG" "infrastructure/modules/iam" +} + +@test "known module: kms included" { + assert_contains "$CONFIG" "infrastructure/modules/kms" +} + +@test "known module: secrets-manager included" { + assert_contains "$CONFIG" "infrastructure/modules/secrets-manager" +} + +@test "foundation module: tags included" { + assert_contains "$CONFIG" "infrastructure/modules/tags" +} + +# --- Exclusions --- + +@test "cache directories excluded" { + assert_not_contains "$CONFIG" ".terraform" +} + +# --- Schedule --- + +@test "weekly schedule configured" { + echo "$CONFIG" | grep -A1 'schedule:' | grep -q 'interval: "weekly"' +} + +# --- Idempotency --- + +@test "script output is idempotent" { + local output_2="$FIXTURE_ROOT/dependabot2.yaml" + bash "$REPO_ROOT/$SCRIPT" "$output_2" > /dev/null 2>&1 + diff -q "$OUTPUT_FILE" "$output_2" +} + +# --- Module count verification --- + +@test "all modules with versions.tf accounted for" { + local actual_count expected_count + actual_count=$(find "$REPO_ROOT/infrastructure/modules" -name "versions.tf" -type f | grep -v '/.terraform/' | wc -l | tr -d ' ') + expected_count=$(echo "$CONFIG" | grep -c '^ - "infrastructure/modules/' || echo 0) + [ "$actual_count" -eq "$expected_count" ] +} + +# --- Template customization preservation --- + +@test "manual non-Terraform customization preserved" { + local template_copy="$FIXTURE_ROOT/dependabot-template.yaml" + local custom_output="$FIXTURE_ROOT/dependabot-custom.yaml" + cp "$REPO_ROOT/.github/dependabot.yaml" "$template_copy" + + # Simulate manual edit outside autogenerated section + sed 's|/scripts/docker/tests|/scripts/docker/tests-custom|g' "$template_copy" > "${template_copy}.tmp" + mv "${template_copy}.tmp" "$template_copy" + + DEPENDABOT_TEMPLATE_FILE="$template_copy" bash "$REPO_ROOT/$SCRIPT" "$custom_output" > /dev/null 2>&1 + assert_file_contains "$custom_output" "/scripts/docker/tests-custom" +} + +# --- Documentation lint (optional) --- + +@test "markdownlint passes for key docs" { + if ! command -v markdownlint >/dev/null 2>&1; then + skip "markdownlint not available" + fi + run markdownlint "$REPO_ROOT/README.md" "$REPO_ROOT/tests/README.md" \ + --config "$REPO_ROOT/scripts/config/markdownlint.yaml" + [ "$status" -eq 0 ] +} + +@test "vale passes for key docs" { + if ! command -v vale >/dev/null 2>&1; then + skip "vale not available" + fi + run vale --config "$REPO_ROOT/scripts/config/vale/vale.ini" \ + "$REPO_ROOT/README.md" "$REPO_ROOT/tests/README.md" + [ "$status" -eq 0 ] +} diff --git a/tests/test-generate-dependabot-config.sh b/tests/test-generate-dependabot-config.sh index 70979686..c346bbca 100644 --- a/tests/test-generate-dependabot-config.sh +++ b/tests/test-generate-dependabot-config.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# DEPRECATED: This test file has been replaced by test-generate-dependabot-config.bats +# which uses the bats-core test framework. Run: bats tests/test-generate-dependabot-config.bats ################################################################################ # Test suite for scripts/generate-dependabot-config.sh # diff --git a/tests/test-mise-task-surface.bats b/tests/test-mise-task-surface.bats new file mode 100644 index 00000000..8497f4a6 --- /dev/null +++ b/tests/test-mise-task-surface.bats @@ -0,0 +1,112 @@ +#!/usr/bin/env bats +# Test suite for mise task surface policy. +# +# Validates that all `mise run` references in automation/docs point to active +# tasks and no commented-out tasks are referenced. + +load test_helper/assertions + +setup() { + MISE_FILE="mise.toml" + + if [[ ! -f "$MISE_FILE" ]]; then + echo "Expected $MISE_FILE to exist" >&2 + return 1 + fi + + # Tasks intentionally kept active for developer/manual use. + ALLOWED_MANUAL_ACTIVE_TASKS=( + "terraform-derive-module-constraints" + "terraform-wrapper" + ) + + # Discover active tasks (uncommented [tasks.xxx] blocks) + mapfile -t ACTIVE_TASKS < <(grep -E '^\[tasks\.[^]]+\]$' "$MISE_FILE" | sed -E 's/^\[tasks\.([^]]+)\]$/\1/' | sort -u) + + # Discover commented-out tasks (# [tasks.xxx] blocks) + mapfile -t COMMENTED_TASKS < <(grep -E '^# \[tasks\.[^]]+\]$' "$MISE_FILE" | sed -E 's/^# \[tasks\.([^]]+)\]$/\1/' | sort -u) + + # Discover all referenced tasks across automation, docs, and tests + mapfile -t REFERENCED_TASKS < <( + grep -rEhn 'mise run [A-Za-z0-9._-]+' \ + .github/workflows \ + .github/actions \ + .pre-commit-config.yaml \ + README.md \ + tests \ + | grep -v 'test-mise-task-surface' \ + | sed -E 's/.*mise run ([A-Za-z0-9._-]+).*/\1/' \ + | sort -u + ) +} + +contains() { + local needle="$1" + shift + local item + for item in "$@"; do + if [[ "$item" == "$needle" ]]; then + return 0 + fi + done + return 1 +} + +# --- Every referenced task must be active --- + +@test "all referenced tasks are active in mise.toml" { + local failures=() + for task in "${REFERENCED_TASKS[@]}"; do + if ! contains "$task" "${ACTIVE_TASKS[@]}"; then + failures+=("$task") + fi + done + if [[ ${#failures[@]} -gt 0 ]]; then + echo "Referenced tasks not active: ${failures[*]}" >&2 + return 1 + fi +} + +# --- Every active task must be referenced or explicitly allowed --- + +@test "all active tasks are referenced or allowed-manual" { + local failures=() + for task in "${ACTIVE_TASKS[@]}"; do + if ! contains "$task" "${REFERENCED_TASKS[@]}" && ! contains "$task" "${ALLOWED_MANUAL_ACTIVE_TASKS[@]}"; then + failures+=("$task") + fi + done + if [[ ${#failures[@]} -gt 0 ]]; then + echo "Active tasks not referenced or allowed: ${failures[*]}" >&2 + return 1 + fi +} + +# --- No commented-out task may be referenced --- + +@test "no commented-out task is referenced" { + local failures=() + for task in "${COMMENTED_TASKS[@]}"; do + if contains "$task" "${REFERENCED_TASKS[@]}"; then + failures+=("$task") + fi + done + if [[ ${#failures[@]} -gt 0 ]]; then + echo "Commented tasks are still referenced: ${failures[*]}" >&2 + return 1 + fi +} + +# --- Sanity checks --- + +@test "at least one active task found" { + [[ ${#ACTIVE_TASKS[@]} -gt 0 ]] +} + +@test "at least one referenced task found" { + [[ ${#REFERENCED_TASKS[@]} -gt 0 ]] +} + +@test "at least one commented task found" { + [[ ${#COMMENTED_TASKS[@]} -gt 0 ]] +} diff --git a/tests/test-mise-task-surface.sh b/tests/test-mise-task-surface.sh index 7bff221d..61fb3300 100755 --- a/tests/test-mise-task-surface.sh +++ b/tests/test-mise-task-surface.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# DEPRECATED: This test file has been replaced by test-mise-task-surface.bats +# which uses the bats-core test framework. Run: bats tests/test-mise-task-surface.bats set -euo pipefail @@ -54,8 +56,8 @@ if [[ ! -f "$MiseFile" ]]; then exit 1 fi -mapfile -t active_tasks < <(rg '^\[tasks\.([^\]]+)\]$' "$MiseFile" -or '$1' | sort -u) -mapfile -t commented_tasks < <(rg '^# \[tasks\.([^\]]+)\]$' "$MiseFile" -or '$1' | sort -u) +mapfile -t active_tasks < <(grep -E '^\[tasks\.[^]]+\]$' "$MiseFile" | sed -E 's/^\[tasks\.([^]]+)\]$/\1/' | sort -u) +mapfile -t commented_tasks < <(grep -E '^# \[tasks\.[^]]+\]$' "$MiseFile" | sed -E 's/^# \[tasks\.([^]]+)\]$/\1/' | sort -u) if [[ "${#active_tasks[@]}" -eq 0 ]]; then echo "No active tasks were found in $MiseFile" >&2 @@ -63,12 +65,13 @@ if [[ "${#active_tasks[@]}" -eq 0 ]]; then fi mapfile -t referenced_tasks < <( - rg -n 'mise run [A-Za-z0-9._-]+' \ + grep -rEhn 'mise run [A-Za-z0-9._-]+' \ .github/workflows \ .github/actions \ .pre-commit-config.yaml \ README.md \ tests \ + | grep -v 'test-mise-task-surface' \ | sed -E 's/.*mise run ([A-Za-z0-9._-]+).*/\1/' \ | sort -u ) diff --git a/tests/test-module-upgrade.bats b/tests/test-module-upgrade.bats new file mode 100644 index 00000000..cd8029c8 --- /dev/null +++ b/tests/test-module-upgrade.bats @@ -0,0 +1,103 @@ +#!/usr/bin/env bats +# Test suite for scripts/terraform/upgrade-module.sh +# +# Tests the Terraform module upgrade helper using mock binaries. + +load test_helper/assertions + +setup_file() { + export REPO_ROOT + REPO_ROOT="$(git rev-parse --show-toplevel)" + export SCRIPT="scripts/terraform/upgrade-module.sh" + + export FIXTURE_ROOT + FIXTURE_ROOT="$(mktemp -d "$REPO_ROOT/tmp/upgrade-module-test.XXXXXX")" + export RELATIVE_FIXTURE_ROOT="${FIXTURE_ROOT#"${REPO_ROOT}"/}" + + export BIN_ROOT + BIN_ROOT="$(mktemp -d)" + + export LOG_FILE="$FIXTURE_ROOT/calls.log" + + # Create mock terraform binary + cat > "$BIN_ROOT/terraform" <> "$LOG_FILE" +exit 0 +EOF + + # Create mock pre-commit binary + cat > "$BIN_ROOT/pre-commit" <> "$LOG_FILE" +exit 0 +EOF + + chmod +x "$BIN_ROOT/terraform" "$BIN_ROOT/pre-commit" + + # Create fixture module directories + mkdir -p "$FIXTURE_ROOT/infrastructure/modules/example-one" + mkdir -p "$FIXTURE_ROOT/infrastructure/modules/example-two" + + cat > "$FIXTURE_ROOT/infrastructure/modules/example-one/main.tf" <<'EOF' +terraform { + required_version = ">= 1.13" +} +EOF + + cat > "$FIXTURE_ROOT/infrastructure/modules/example-one/README.md" <<'EOF' +# Example one +EOF + + cat > "$FIXTURE_ROOT/infrastructure/modules/example-two/main.tf" <<'EOF' +terraform { + required_version = ">= 1.13" +} +EOF + + cat > "$FIXTURE_ROOT/infrastructure/modules/example-two/readme.md" <<'EOF' +# Example two +EOF +} + +teardown_file() { + rm -rf "$FIXTURE_ROOT" "$BIN_ROOT" +} + +# --- Single-module upgrade --- + +@test "single-module: init uses -upgrade flag" { + : > "$LOG_FILE" + PATH="$BIN_ROOT:$PATH" bash "$REPO_ROOT/$SCRIPT" "$FIXTURE_ROOT/infrastructure/modules/example-one" + assert_file_contains "$LOG_FILE" "terraform:-chdir=$RELATIVE_FIXTURE_ROOT/infrastructure/modules/example-one init -upgrade" +} + +@test "single-module: providers lock runs with all target platforms" { + assert_file_contains "$LOG_FILE" "terraform:-chdir=$RELATIVE_FIXTURE_ROOT/infrastructure/modules/example-one providers lock -platform=linux_arm64 -platform=linux_amd64 -platform=darwin_arm64 -platform=darwin_amd64 -platform=windows_amd64" +} + +@test "single-module: terraform_docs runs against README.md" { + assert_file_contains "$LOG_FILE" "pre-commit:run terraform_docs --files $RELATIVE_FIXTURE_ROOT/infrastructure/modules/example-one/README.md" +} + +# --- Update-all upgrade --- + +@test "update-all: touches a lowercase-docs module" { + : > "$LOG_FILE" + PATH="$BIN_ROOT:$PATH" bash "$REPO_ROOT/$SCRIPT" update-all <<< 'yes' >/dev/null + assert_file_contains "$LOG_FILE" "terraform:-chdir=infrastructure/modules/api-gateway init -upgrade" +} + +@test "update-all: touches an uppercase-docs module" { + assert_file_contains "$LOG_FILE" "terraform:-chdir=infrastructure/modules/license-manager init -upgrade" +} + +@test "update-all: uses README.md symlink for lowercase docs" { + assert_file_contains "$LOG_FILE" "pre-commit:run terraform_docs --files infrastructure/modules/api-gateway/README.md" +} + +@test "update-all: processes modules sequentially" { + assert_line_order "$LOG_FILE" \ + "terraform:-chdir=infrastructure/modules/api-gateway init -upgrade" \ + "terraform:-chdir=infrastructure/modules/license-manager init -upgrade" +} diff --git a/tests/test-module-upgrade.sh b/tests/test-module-upgrade.sh index ab508e6f..87a0826d 100755 --- a/tests/test-module-upgrade.sh +++ b/tests/test-module-upgrade.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# DEPRECATED: This test file has been replaced by test-module-upgrade.bats +# which uses the bats-core test framework. Run: bats tests/test-module-upgrade.bats set -euo pipefail diff --git a/tests/test-tool-version-upgrade.bats b/tests/test-tool-version-upgrade.bats new file mode 100644 index 00000000..ebd8cddc --- /dev/null +++ b/tests/test-tool-version-upgrade.bats @@ -0,0 +1,352 @@ +#!/usr/bin/env bats +# Test suite for scripts/mise/update-tool-versions.sh +# +# Tests the tool version upgrade helper with mock mise binary. + +load test_helper/assertions + +setup_file() { + export REPO_ROOT + REPO_ROOT="$(git rev-parse --show-toplevel)" + export SCRIPT="scripts/mise/update-tool-versions.sh" + + mkdir -p "$REPO_ROOT/tmp" + export FIXTURE_ROOT + FIXTURE_ROOT="$(mktemp -d "$REPO_ROOT/tmp/tool-version-upgrade-test.XXXXXX")" + + export BIN_ROOT + BIN_ROOT="$(mktemp -d)" + + export LOG_FILE="$FIXTURE_ROOT/calls.log" + export OUTPUT_FILE="$FIXTURE_ROOT/command-output.log" + + # Create mock mise binary + cat > "$BIN_ROOT/mise" <<'SCRIPT' +#!/usr/bin/env bash +set -euo pipefail +log_file="${MISE_TEST_LOG:?}" +repo_root="${REPO_ROOT:?}" + +printf '%s\n' "mise:$*" >> "$log_file" + +case "$1" in + outdated) + if [[ "${2:-}" == "--local" && "${3:-}" == "--bump" && "${4:-}" == "--json" ]]; then + cat <<'JSON' +{"terraform":{"current":"1.13.2","latest":"1.13.3"},"python":{"current":"3.12.0","latest":"3.13.0"},"node":{"current":"20.11.0","latest":"21.1.0"},"go:github.com/hashicorp/terraform-config-inspect":{"current":"latest","latest":"latest"}} +JSON + else + echo "terraform 1.13.2 -> 1.13.3" + echo "python 3.12.0 -> 3.13.0" + echo "node 20.11.0 -> 21.1.0" + fi + ;; + install) + ;; + upgrade) + if [[ "$*" == "upgrade --local --bump terraform" ]]; then + perl -0pi -e 's/terraform = "1\.13\.2"/terraform = "1.13.3"/' "$repo_root/mise.toml" + elif [[ "$*" == "upgrade --local --bump python" ]]; then + perl -0pi -e 's/python = "3\.12\.0"/python = "3.13.0"/' "$repo_root/mise.toml" + elif [[ "$*" == "upgrade --local --bump node" ]]; then + perl -0pi -e 's/node = "20\.11\.0"/node = "21.1.0"/' "$repo_root/mise.toml" + else + perl -0pi -e 's/terraform = "1\.13\.2"/terraform = "1.13.3"/' "$repo_root/mise.toml" + perl -0pi -e 's/python = "3\.12\.0"/python = "3.13"/' "$repo_root/mise.toml" + perl -0pi -e 's/node = "20\.11\.0"/node = "21.1.0"/' "$repo_root/mise.toml" + fi + ;; + lock) + cat > "$repo_root/mise.lock" <<'LOCK' +# regenerated lock +LOCK + ;; + *) + echo "unexpected mise command: $*" >&2 + exit 1 + ;; +esac +SCRIPT + + chmod +x "$BIN_ROOT/mise" +} + +teardown_file() { + rm -rf "$FIXTURE_ROOT" "$BIN_ROOT" +} + +write_fixture_files() { + mkdir -p "$FIXTURE_ROOT" + + cat > "$FIXTURE_ROOT/mise.toml" <<'EOF' +[settings] +lockfile = true + +[tools] +actionlint = "1.7.12" +gitleaks = "8.30.1" +jq = "1.8.1" +make = "4.4.1" +node = "20.11.0" +pre-commit = "4.6.0" +python = "3.12.0" +shellcheck = "0.11.0" +terraform = "1.13.2" +terraform-docs = "0.24.0" +tflint = "0.62.1" +vale = "3.6.0" +yq = "4.53.3" +"go:github.com/hashicorp/terraform-config-inspect" = "latest" +EOF + + cat > "$FIXTURE_ROOT/.tool-versions" <<'EOF' +actionlint 1.7.12 +gitleaks 8.30.1 +go:github.com/hashicorp/terraform-config-inspect latest +jq 1.8.1 +make 4.4.1 +node 20.11.0 +pre-commit 4.6.0 +python 3.12.0 +shellcheck 0.11.0 +terraform 1.13.2 +terraform-docs 0.24.0 +tflint 0.62.1 +vale 3.6.0 +yq 4.53.3 +EOF + + cat > "$FIXTURE_ROOT/mise.lock" <<'EOF' +# old lock +EOF + + cat > "$FIXTURE_ROOT/README.md" <<'EOF' +# Fixture README + + +| Tool | Version | Purpose | +| --- | --- | --- | + +EOF +} + +run_helper() { + : > "$LOG_FILE" + write_fixture_files + local helper_script + helper_script="$REPO_ROOT/$SCRIPT" + MISE_TEST_LOG="$LOG_FILE" REPO_ROOT="$FIXTURE_ROOT" PATH="$BIN_ROOT:$PATH" \ + bash "$helper_script" "$@" >"$OUTPUT_FILE" 2>&1 +} + +# --- Default run --- + +@test "default: runs mise install" { + run_helper + assert_file_contains "$LOG_FILE" "mise:install" +} + +@test "default: runs mise upgrade with local bump" { + run_helper + assert_file_contains "$LOG_FILE" "mise:upgrade --local --bump" +} + +@test "default: regenerates lockfile" { + run_helper + assert_file_contains "$LOG_FILE" "mise:lock" +} + +@test "default: syncs terraform version to .tool-versions" { + run_helper + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "terraform 1.13.3" +} + +@test "default: syncs python version to .tool-versions" { + run_helper + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "python 3.13" +} + +@test "default: syncs node version to .tool-versions" { + run_helper + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "node 21.1.0" +} + +@test "default: preserves tool aliases from mise.toml" { + run_helper + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "go:github.com/hashicorp/terraform-config-inspect latest" +} + +@test "default: removes stale terraform version from .tool-versions" { + run_helper + assert_file_not_contains "$FIXTURE_ROOT/.tool-versions" "terraform 1.13.2" +} + +@test "default: rewrites mise.lock" { + run_helper + assert_file_contains "$FIXTURE_ROOT/mise.lock" "# regenerated lock" +} + +# --- Dry-run --- + +@test "dry-run: calls mise outdated only" { + run_helper --dry-run + assert_file_contains "$LOG_FILE" "mise:outdated --local --bump" +} + +@test "dry-run: does not run upgrades" { + run_helper --dry-run + assert_file_not_contains "$LOG_FILE" "mise:upgrade --local --bump" +} + +@test "dry-run: leaves .tool-versions unchanged" { + run_helper --dry-run + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "terraform 1.13.2" +} + +# --- Patch-level filter --- + +@test "patch-only: reads outdated JSON" { + run_helper --upgrade-level patch + assert_file_contains "$LOG_FILE" "mise:outdated --local --bump --json" +} + +@test "patch-only: upgrades only patch-level tools" { + run_helper --upgrade-level patch + assert_file_contains "$LOG_FILE" "mise:upgrade --local --bump terraform" +} + +@test "patch-only: prints planned upgrade heading" { + run_helper --upgrade-level patch + assert_file_contains "$OUTPUT_FILE" "Planned patch upgrades:" +} + +@test "patch-only: shows from/to versions" { + run_helper --upgrade-level patch + assert_file_contains "$OUTPUT_FILE" "terraform: 1.13.2 -> 1.13.3" +} + +@test "patch-only: updates terraform" { + run_helper --upgrade-level patch + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "terraform 1.13.3" +} + +@test "patch-only: leaves python unchanged" { + run_helper --upgrade-level patch + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "python 3.12.0" +} + +@test "patch-only: leaves node unchanged" { + run_helper --upgrade-level patch + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "node 20.11.0" +} + +# --- Minor-level filter --- + +@test "minor-only: reads outdated JSON" { + run_helper --upgrade-level minor + assert_file_contains "$LOG_FILE" "mise:outdated --local --bump --json" +} + +@test "minor-only: upgrades only minor-level tools" { + run_helper --upgrade-level minor + assert_file_contains "$LOG_FILE" "mise:upgrade --local --bump python" +} + +@test "minor-only: prints planned upgrade heading" { + run_helper --upgrade-level minor + assert_file_contains "$OUTPUT_FILE" "Planned minor upgrades:" +} + +@test "minor-only: shows from/to versions" { + run_helper --upgrade-level minor + assert_file_contains "$OUTPUT_FILE" "python: 3.12.0 -> 3.13.0" +} + +@test "minor-only: updates python" { + run_helper --upgrade-level minor + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "python 3.13.0" +} + +@test "minor-only: leaves terraform unchanged" { + run_helper --upgrade-level minor + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "terraform 1.13.2" +} + +@test "minor-only: leaves node unchanged" { + run_helper --upgrade-level minor + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "node 20.11.0" +} + +# --- Major-level filter --- + +@test "major-only: reads outdated JSON" { + run_helper --upgrade-level major + assert_file_contains "$LOG_FILE" "mise:outdated --local --bump --json" +} + +@test "major-only: upgrades only major-level tools" { + run_helper --upgrade-level major + assert_file_contains "$LOG_FILE" "mise:upgrade --local --bump node" +} + +@test "major-only: prints planned upgrade heading" { + run_helper --upgrade-level major + assert_file_contains "$OUTPUT_FILE" "Planned major upgrades:" +} + +@test "major-only: shows from/to versions" { + run_helper --upgrade-level major + assert_file_contains "$OUTPUT_FILE" "node: 20.11.0 -> 21.1.0" +} + +@test "major-only: updates node" { + run_helper --upgrade-level major + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "node 21.1.0" +} + +@test "major-only: leaves terraform unchanged" { + run_helper --upgrade-level major + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "terraform 1.13.2" +} + +@test "major-only: leaves python unchanged" { + run_helper --upgrade-level major + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "python 3.12.0" +} + +# --- Explicit all --- + +@test "explicit-all: installs tools" { + run_helper --upgrade-level all + assert_file_contains "$LOG_FILE" "mise:install" +} + +@test "explicit-all: upgrades without per-tool filter" { + run_helper --upgrade-level all + assert_file_contains "$LOG_FILE" "mise:upgrade --local --bump" +} + +@test "explicit-all: prints planned upgrade heading" { + run_helper --upgrade-level all + assert_file_contains "$OUTPUT_FILE" "Planned upgrades (all levels):" +} + +@test "explicit-all: shows from/to versions" { + run_helper --upgrade-level all + assert_file_contains "$OUTPUT_FILE" "terraform 1.13.2 -> 1.13.3" +} + +@test "explicit-all: updates terraform" { + run_helper --upgrade-level all + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "terraform 1.13.3" +} + +@test "explicit-all: updates python" { + run_helper --upgrade-level all + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "python 3.13" +} + +@test "explicit-all: updates node" { + run_helper --upgrade-level all + assert_file_contains "$FIXTURE_ROOT/.tool-versions" "node 21.1.0" +} diff --git a/tests/test-tool-version-upgrade.sh b/tests/test-tool-version-upgrade.sh index 1694355c..98c5edef 100755 --- a/tests/test-tool-version-upgrade.sh +++ b/tests/test-tool-version-upgrade.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# DEPRECATED: This test file has been replaced by test-tool-version-upgrade.bats +# which uses the bats-core test framework. Run: bats tests/test-tool-version-upgrade.bats set -euo pipefail @@ -32,27 +34,37 @@ write_fixture_files() { lockfile = true [tools] -terraform = "1.13.2" -tflint = "0.62.1" -terraform-docs = "0.24.0" +actionlint = "1.7.12" +gitleaks = "8.30.1" +jq = "1.8.1" +make = "4.4.1" +node = "20.11.0" pre-commit = "4.6.0" python = "3.12.0" -vale = "3.6.0" -gitleaks = "8.30.1" shellcheck = "0.11.0" -actionlint = "1.7.12" -jq = "1.8.1" +terraform = "1.13.2" +terraform-docs = "0.24.0" +tflint = "0.62.1" +vale = "3.6.0" yq = "4.53.3" -node = "20.11.0" -make = "4.4.1" "go:github.com/hashicorp/terraform-config-inspect" = "latest" EOF cat > "$fixture_root/.tool-versions" <<'EOF' -terraform 1.13.2 -python 3.12.0 -node 20.11.0 +actionlint 1.7.12 +gitleaks 8.30.1 go:github.com/hashicorp/terraform-config-inspect latest +jq 1.8.1 +make 4.4.1 +node 20.11.0 +pre-commit 4.6.0 +python 3.12.0 +shellcheck 0.11.0 +terraform 1.13.2 +terraform-docs 0.24.0 +tflint 0.62.1 +vale 3.6.0 +yq 4.53.3 EOF cat > "$fixture_root/mise.lock" <<'EOF' diff --git a/tests/test-workflow-security.bats b/tests/test-workflow-security.bats new file mode 100644 index 00000000..519bc6cb --- /dev/null +++ b/tests/test-workflow-security.bats @@ -0,0 +1,112 @@ +#!/usr/bin/env bats +# Test suite for verifying GitHub Actions workflow security pinning. +# +# Validates that workflow files use immutable action references (commit SHAs) +# and have version comments for human readability. + +load test_helper/assertions + +# --- stage-1-pre-commit.yml --- + +@test "stage-1-pre-commit.yml: actions/checkout pinned to SHA" { + assert_file_matches ".github/workflows/stage-1-pre-commit.yml" "uses:.*actions/checkout@[0-9a-f]{40}.*#" +} + +@test "stage-1-pre-commit.yml: jdx/mise-action pinned to SHA" { + assert_file_matches ".github/workflows/stage-1-pre-commit.yml" "uses:.*jdx/mise-action@[0-9a-f]{40}.*#" +} + +@test "stage-1-pre-commit.yml: actions/cache pinned to SHA" { + assert_file_matches ".github/workflows/stage-1-pre-commit.yml" "uses:.*actions/cache@[0-9a-f]{40}.*#" +} + +@test "stage-1-pre-commit.yml: AWS region configuration present" { + assert_file_contains ".github/workflows/stage-1-pre-commit.yml" "AWS_DEFAULT_REGION" +} + +@test "stage-1-pre-commit.yml: Terraform plugin cache configured" { + assert_file_contains ".github/workflows/stage-1-pre-commit.yml" "TF_PLUGIN_CACHE_DIR" +} + +# --- dependency-tools-mise-upgrade.yml --- + +@test "dependency-tools-mise-upgrade.yml: actions/checkout pinned to SHA" { + assert_file_matches ".github/workflows/dependency-tools-mise-upgrade.yml" "uses:.*actions/checkout@[0-9a-f]{40}.*#" +} + +@test "dependency-tools-mise-upgrade.yml: jdx/mise-action pinned to SHA" { + assert_file_matches ".github/workflows/dependency-tools-mise-upgrade.yml" "uses:.*jdx/mise-action@[0-9a-f]{40}.*#" +} + +@test "dependency-tools-mise-upgrade.yml: peter-evans/create-pull-request pinned to SHA" { + assert_file_matches ".github/workflows/dependency-tools-mise-upgrade.yml" "uses:.*peter-evans/create-pull-request@[0-9a-f]{40}.*#" +} + +@test "dependency-tools-mise-upgrade.yml: uses mise run update-tool-versions" { + assert_file_contains ".github/workflows/dependency-tools-mise-upgrade.yml" "mise run update-tool-versions" +} + +# --- Pre-commit configuration --- + +@test "pre-commit config: repos pinned to commit SHAs" { + assert_file_matches ".pre-commit-config.yaml" "rev:.*[0-9a-f]{40}" +} + +@test "pre-commit config: version comments for readability" { + assert_file_matches ".pre-commit-config.yaml" "#.*v[0-9]" +} + +@test "pre-commit config: local conventional commit validator" { + assert_file_contains ".pre-commit-config.yaml" "scripts/githooks/validate-conventional-commit.sh" +} + +@test "pre-commit config: provider generator uses mise task" { + assert_file_contains ".pre-commit-config.yaml" "mise run githooks-generate-terraform-providers" +} + +@test "pre-commit config: shellcheck hook uses wrapper task" { + assert_file_contains ".pre-commit-config.yaml" "mise run shellscript-linter" +} + +@test "shellcheck wrapper supports Docker fallback override" { + assert_file_contains "scripts/shellscript-linter.sh" "FORCE_USE_DOCKER" +} + +# --- Custom hooks implementation --- + +@test "conventional commit validator script is executable" { + [ -x "scripts/githooks/validate-conventional-commit.sh" ] +} + +@test "provider generator script is executable" { + [ -x "scripts/githooks/generate-terraform-providers.sh" ] +} + +# --- Tool version file consistency --- + +@test ".tool-versions file exists" { + assert_file_exists ".tool-versions" +} + +@test "mise.toml file exists" { + assert_file_exists "mise.toml" +} + +@test "mise.lock file exists" { + assert_file_exists "mise.lock" +} + +@test "terraform version in sync between .tool-versions and mise.toml" { + local tv_version mt_version + tv_version=$(grep "^terraform " ".tool-versions" | awk '{print $2}') + mt_version=$(grep 'terraform.*=' "mise.toml" | grep -v '#' | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) + [ "$tv_version" = "$mt_version" ] +} + +@test "pre-commit version in .tool-versions" { + assert_file_matches ".tool-versions" "pre-commit 4\\.6\\.0" +} + +@test "pre-commit version in mise.toml" { + assert_file_matches "mise.toml" "pre-commit.*4\\.6\\.0" +} diff --git a/tests/test-workflow-security.sh b/tests/test-workflow-security.sh index 26491ced..a2fc6451 100755 --- a/tests/test-workflow-security.sh +++ b/tests/test-workflow-security.sh @@ -1,4 +1,7 @@ #!/usr/bin/env bash +# DEPRECATED: This test file has been replaced by test-workflow-security.bats +# which uses the bats-core test framework. Run: bats tests/test-workflow-security.bats +# # Test suite for verifying GitHub Actions workflow security pinning # # Validates that workflow files use immutable action references (commit SHAs) diff --git a/tests/test_helper/assertions.bash b/tests/test_helper/assertions.bash new file mode 100644 index 00000000..641c38e6 --- /dev/null +++ b/tests/test_helper/assertions.bash @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +# Shared assertion helpers for bats tests. +# Load in .bats files with: load test_helper/assertions + +# Assert a file contains a fixed string. +# Usage: assert_file_contains +assert_file_contains() { + local file="$1" + local needle="$2" + if ! grep -Fq -- "$needle" "$file"; then + echo "Expected file to contain: $needle" >&2 + echo " File: $file" >&2 + return 1 + fi +} + +# Assert a file does NOT contain a fixed string. +# Usage: assert_file_not_contains +assert_file_not_contains() { + local file="$1" + local needle="$2" + if grep -Fq -- "$needle" "$file"; then + echo "Expected file NOT to contain: $needle" >&2 + echo " File: $file" >&2 + return 1 + fi +} + +# Assert a file exists. +# Usage: assert_file_exists +assert_file_exists() { + local file="$1" + if [[ ! -f "$file" ]]; then + echo "Expected file to exist: $file" >&2 + return 1 + fi +} + +# Assert first pattern appears before second in a file. +# Usage: assert_line_order +assert_line_order() { + local file="$1" + local first="$2" + local second="$3" + local first_line second_line + first_line="$(grep -nF "$first" "$file" | head -1 | cut -d: -f1 || true)" + second_line="$(grep -nF "$second" "$file" | head -1 | cut -d: -f1 || true)" + if [[ -z "$first_line" || -z "$second_line" || "$first_line" -ge "$second_line" ]]; then + echo "Expected '$first' (line ${first_line:-?}) before '$second' (line ${second_line:-?})" >&2 + echo " File: $file" >&2 + return 1 + fi +} + +# Assert a string/variable contains a substring. +# Usage: assert_contains +assert_contains() { + local haystack="$1" + local needle="$2" + if ! echo "$haystack" | grep -Fq -- "$needle"; then + echo "Expected output to contain: $needle" >&2 + return 1 + fi +} + +# Assert a string/variable does NOT contain a substring. +# Usage: assert_not_contains +assert_not_contains() { + local haystack="$1" + local needle="$2" + if echo "$haystack" | grep -Fq -- "$needle"; then + echo "Expected output NOT to contain: $needle" >&2 + return 1 + fi +} + +# Assert a regex pattern matches within a file. +# Usage: assert_file_matches +assert_file_matches() { + local file="$1" + local pattern="$2" + if ! grep -qE "$pattern" "$file"; then + echo "Expected file to match pattern: $pattern" >&2 + echo " File: $file" >&2 + return 1 + fi +} From 716f22807015bdc559229ecd95b11b6a5193110a Mon Sep 17 00:00:00 2001 From: Oliver Slater Date: Fri, 19 Jun 2026 14:25:37 +0100 Subject: [PATCH 3/3] chore(mise): parse tool metadata from structured comments Replace static prerequisite metadata with @name/@url/@purpose/@prefix annotations in mise.toml and parse them in update-tool-versions.sh. Also remove mapfile usage for Bash 3.2 compatibility in scripts/tests and regenerate lock/version artefacts and related tests. --- mise.lock | 90 ++++++++--------- mise.toml | 19 +++- scripts/mise/update-tool-versions.sh | 138 +++++++++++++++++++-------- scripts/terraform/upgrade-module.sh | 5 +- tests/test-mise-task-surface.bats | 15 ++- tests/test-mise-task-surface.sh | 16 +++- tests/test-tool-version-upgrade.bats | 11 +++ tests/test-tool-version-upgrade.sh | 2 + 8 files changed, 201 insertions(+), 95 deletions(-) diff --git a/mise.lock b/mise.lock index 1c4936a6..30dcc854 100644 --- a/mise.lock +++ b/mise.lock @@ -328,42 +328,42 @@ checksum = "sha256:ea8a0c84902e48c1875558f2f362ed8476773aa5fc8c16c5d8f2acc2a2830 url = "https://github.com/pre-commit/pre-commit/releases/download/v4.6.0/pre-commit-4.6.0.pyz" [[tools.python]] -version = "3.12.13" +version = "3.14.6" backend = "core:python" [tools.python."platforms.linux-arm64"] -checksum = "sha256:eb85c6903552b1cb40b183a119681a9326fa97e31cf0e4b8c78bf26735ff1552" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260610/cpython-3.12.13+20260610-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz" +checksum = "sha256:9124d5c41739d78a446f1f29c1953bf41071abcbf18e6daec201a27608648195" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260610/cpython-3.14.6+20260610-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz" provenance = "github-attestations" [tools.python."platforms.linux-arm64-musl"] -checksum = "sha256:f9153de0832a08a98cde89f7f5f2cc376544813c05bd2737279a1c8e3cb97913" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260610/cpython-3.12.13+20260610-aarch64-unknown-linux-musl-install_only_stripped.tar.gz" +checksum = "sha256:5c75bea22f425ebc94912c618518a5aa4753eedeea6b5da5939f027d3a9c0fec" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260610/cpython-3.14.6+20260610-aarch64-unknown-linux-musl-install_only_stripped.tar.gz" provenance = "github-attestations" [tools.python."platforms.linux-x64"] -checksum = "sha256:6682afef6b510037a0ad84e61150e5121af2e2785c9ca27b047e029270a840fe" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260610/cpython-3.12.13+20260610-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz" +checksum = "sha256:0881fbde2c78605720fb2e240d1a6df38652a896e40a5fce2e10908dbee5f5b7" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260610/cpython-3.14.6+20260610-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz" provenance = "github-attestations" [tools.python."platforms.linux-x64-musl"] -checksum = "sha256:cca04a9f21a8efb43f1da1f4d1b55da04e9af23e8ce6259f58c94b4ef1fa48a6" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260610/cpython-3.12.13+20260610-x86_64-unknown-linux-musl-install_only_stripped.tar.gz" +checksum = "sha256:54eca143d09ed3c596ecad5e3bfcb7724387818f8f984f44f3d8c0e9f36681d3" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260610/cpython-3.14.6+20260610-x86_64-unknown-linux-musl-install_only_stripped.tar.gz" provenance = "github-attestations" [tools.python."platforms.macos-arm64"] -checksum = "sha256:f0a7fa7decc75df2b1a789329a44f657c4a15c0a683f197ce46a5cb621bc6ef4" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260610/cpython-3.12.13+20260610-aarch64-apple-darwin-install_only_stripped.tar.gz" +checksum = "blake3:0677b851c7d6b3f89667b4ac7a49a48b960eba913a74c34724a2decc3c56367a" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260610/cpython-3.14.6+20260610-aarch64-apple-darwin-install_only_stripped.tar.gz" provenance = "github-attestations" [tools.python."platforms.macos-x64"] -checksum = "sha256:c56c2dfe3fb5569430f4eabcff1fc1334c66db708bf17c103eef3dd237f3e3ab" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260610/cpython-3.12.13+20260610-x86_64-apple-darwin-install_only_stripped.tar.gz" +checksum = "sha256:6f85cbb2a0fef4b90fc63aadcfe383c22db5eb7933066ca6ea384828a6b3f2d2" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260610/cpython-3.14.6+20260610-x86_64-apple-darwin-install_only_stripped.tar.gz" provenance = "github-attestations" [tools.python."platforms.windows-x64"] -checksum = "sha256:99dce0b23bf3c3b28d350cdd7bfe3cd3be51cc4f285faae7c0df110d106d1a8d" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260610/cpython-3.12.13+20260610-x86_64-pc-windows-msvc-install_only_stripped.tar.gz" +checksum = "sha256:49f0c7e8d1c2eaba70c5516ca6c01b6b766f6ff473f1209a76fd01783e7c75ae" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260610/cpython-3.14.6+20260610-x86_64-pc-windows-msvc-install_only_stripped.tar.gz" provenance = "github-attestations" [[tools.shellcheck]] @@ -463,68 +463,68 @@ checksum = "sha256:afc02cbdf63726d3e5fb26a077ca1f24acd4820d7e44c21b03a3e030f9266 url = "https://github.com/terraform-docs/terraform-docs/releases/download/v0.24.0/terraform-docs-v0.24.0-windows-amd64.zip" [[tools.tflint]] -version = "0.62.1" +version = "0.63.1" backend = "aqua:terraform-linters/tflint" [tools.tflint."platforms.linux-arm64"] -checksum = "sha256:9f3bce43f7f58f05ddcf193f0bf1f7e7a9c7a79d7f46f72dd38e97f96fcaf14c" -url = "https://github.com/terraform-linters/tflint/releases/download/v0.62.1/tflint_linux_arm64.zip" +checksum = "sha256:6d858ca7f11858c3fe3c5e29cc746823abccb55e2d2e2da130fa7ad7ea4eecb8" +url = "https://github.com/terraform-linters/tflint/releases/download/v0.63.1/tflint_linux_arm64.zip" [tools.tflint."platforms.linux-arm64-musl"] -checksum = "sha256:9f3bce43f7f58f05ddcf193f0bf1f7e7a9c7a79d7f46f72dd38e97f96fcaf14c" -url = "https://github.com/terraform-linters/tflint/releases/download/v0.62.1/tflint_linux_arm64.zip" +checksum = "sha256:6d858ca7f11858c3fe3c5e29cc746823abccb55e2d2e2da130fa7ad7ea4eecb8" +url = "https://github.com/terraform-linters/tflint/releases/download/v0.63.1/tflint_linux_arm64.zip" [tools.tflint."platforms.linux-x64"] -checksum = "sha256:c004ec45ade3caf87cd4089feb1d2af9f7df57b13140a36df8a63c0a8cc69f14" -url = "https://github.com/terraform-linters/tflint/releases/download/v0.62.1/tflint_linux_amd64.zip" +checksum = "sha256:8441a7d97df20431f19c9b9d27ff4c63e308c964e86660bc7cc0cf7bbe0725e8" +url = "https://github.com/terraform-linters/tflint/releases/download/v0.63.1/tflint_linux_amd64.zip" [tools.tflint."platforms.linux-x64-musl"] -checksum = "sha256:c004ec45ade3caf87cd4089feb1d2af9f7df57b13140a36df8a63c0a8cc69f14" -url = "https://github.com/terraform-linters/tflint/releases/download/v0.62.1/tflint_linux_amd64.zip" +checksum = "sha256:8441a7d97df20431f19c9b9d27ff4c63e308c964e86660bc7cc0cf7bbe0725e8" +url = "https://github.com/terraform-linters/tflint/releases/download/v0.63.1/tflint_linux_amd64.zip" [tools.tflint."platforms.macos-arm64"] -checksum = "sha256:927866fef68382138b8fed038721a03c0928ce9486e1616b18bd3dd11e7cdacb" -url = "https://github.com/terraform-linters/tflint/releases/download/v0.62.1/tflint_darwin_arm64.zip" +checksum = "sha256:6aab157b22367dcab1635b370e98d6e791d9b40b021d4f9baef010d88f53e16b" +url = "https://github.com/terraform-linters/tflint/releases/download/v0.63.1/tflint_darwin_arm64.zip" [tools.tflint."platforms.macos-x64"] -checksum = "sha256:7f55df3a25deb610c267f600eb4247657e3ff776d0322916ceecd7b58142a73a" -url = "https://github.com/terraform-linters/tflint/releases/download/v0.62.1/tflint_darwin_amd64.zip" +checksum = "sha256:1a22782473e4a01f0dd23edc649bed4420655a9e3459ffc06951e698eed7ad01" +url = "https://github.com/terraform-linters/tflint/releases/download/v0.63.1/tflint_darwin_amd64.zip" [tools.tflint."platforms.windows-x64"] -checksum = "sha256:bef472fd62dcbdb616bbdf67800f1bcf8db64598529c09610154686555180edd" -url = "https://github.com/terraform-linters/tflint/releases/download/v0.62.1/tflint_windows_amd64.zip" +checksum = "sha256:5fbfb643b83c4ad489bde15a0e0d46e53dc9aa8dfa76d25da3c4bd2698a41a19" +url = "https://github.com/terraform-linters/tflint/releases/download/v0.63.1/tflint_windows_amd64.zip" [[tools.vale]] -version = "3.6.0" +version = "3.15.1" backend = "aqua:vale-cli/vale" [tools.vale."platforms.linux-arm64"] -checksum = "sha256:45295ab07fd99c87a9cac20dce5d230d7b63ffec1394b38c8d5d4496347a069b" -url = "https://github.com/vale-cli/vale/releases/download/v3.6.0/vale_3.6.0_Linux_arm64.tar.gz" +checksum = "sha256:281a419e140da11a408935356bab7a4ef770fed047a9d7bd1765c76acd647d01" +url = "https://github.com/vale-cli/vale/releases/download/v3.15.1/vale_3.15.1_Linux_arm64.tar.gz" [tools.vale."platforms.linux-arm64-musl"] -checksum = "sha256:45295ab07fd99c87a9cac20dce5d230d7b63ffec1394b38c8d5d4496347a069b" -url = "https://github.com/vale-cli/vale/releases/download/v3.6.0/vale_3.6.0_Linux_arm64.tar.gz" +checksum = "sha256:281a419e140da11a408935356bab7a4ef770fed047a9d7bd1765c76acd647d01" +url = "https://github.com/vale-cli/vale/releases/download/v3.15.1/vale_3.15.1_Linux_arm64.tar.gz" [tools.vale."platforms.linux-x64"] -checksum = "sha256:d27cd03f920202a7be5bba3fd9e784a22fe8b33f838911853588ce3ce594a25d" -url = "https://github.com/vale-cli/vale/releases/download/v3.6.0/vale_3.6.0_Linux_64-bit.tar.gz" +checksum = "sha256:c024d9c157874fb043d4f24a055d60050d1bb18755251f590593eed5bace1857" +url = "https://github.com/vale-cli/vale/releases/download/v3.15.1/vale_3.15.1_Linux_64-bit.tar.gz" [tools.vale."platforms.linux-x64-musl"] -checksum = "sha256:d27cd03f920202a7be5bba3fd9e784a22fe8b33f838911853588ce3ce594a25d" -url = "https://github.com/vale-cli/vale/releases/download/v3.6.0/vale_3.6.0_Linux_64-bit.tar.gz" +checksum = "sha256:c024d9c157874fb043d4f24a055d60050d1bb18755251f590593eed5bace1857" +url = "https://github.com/vale-cli/vale/releases/download/v3.15.1/vale_3.15.1_Linux_64-bit.tar.gz" [tools.vale."platforms.macos-arm64"] -checksum = "sha256:df71e5fcbefca9413479737fa946e105249ec73e2a328ef1784e5558d92414b0" -url = "https://github.com/vale-cli/vale/releases/download/v3.6.0/vale_3.6.0_macOS_arm64.tar.gz" +checksum = "sha256:968c6d8bf2052bc97aa24274234cc466dbcc249b55ace33dd382c2cdfa93b08c" +url = "https://github.com/vale-cli/vale/releases/download/v3.15.1/vale_3.15.1_macOS_arm64.tar.gz" [tools.vale."platforms.macos-x64"] -checksum = "sha256:4d1df61fc3b1e4109bd965b478dad6cb8998e2ca7836100b936c16c331c8c9f2" -url = "https://github.com/vale-cli/vale/releases/download/v3.6.0/vale_3.6.0_macOS_64-bit.tar.gz" +checksum = "sha256:9268383c9e244332c4483cb359e52bd4cb030542873e2cafc48e3bbfff6a989a" +url = "https://github.com/vale-cli/vale/releases/download/v3.15.1/vale_3.15.1_macOS_64-bit.tar.gz" [tools.vale."platforms.windows-x64"] -checksum = "sha256:6b205cd9ee4df016d42287cd0b2b8f234acb45900fc371cda97a5c49bc69b350" -url = "https://github.com/vale-cli/vale/releases/download/v3.6.0/vale_3.6.0_Windows_64-bit.zip" +checksum = "sha256:3395fca0ddfb10a9b6caa28e091d5df709b1d6b6579afb7dece852cad89b94f3" +url = "https://github.com/vale-cli/vale/releases/download/v3.15.1/vale_3.15.1_Windows_64-bit.zip" [[tools.yq]] version = "4.53.3" diff --git a/mise.toml b/mise.toml index dc2d2487..a8a251b3 100644 --- a/mise.toml +++ b/mise.toml @@ -5,23 +5,36 @@ auto_install = true idiomatic_version_file_enable_tools = ["node", "python", "terraform"] [tools] +# @name=actionlint @url=https://github.com/rhysd/actionlint @purpose=GitHub Actions linter actionlint = "1.7.12" +# @name=bats-core @url=https://github.com/bats-core/bats-core @purpose=Bash test framework bats = "1.13.0" +# @name=Gitleaks @url=https://github.com/gitleaks/gitleaks @purpose=Secret scanning gitleaks = "8.30.1" go = "1.26.4" # needed for installing go-based tools from source +# @name=jq @url=https://jqlang.github.io/jq/ @purpose=JSON processor jq = "1.8.1" +# @name=GNU make @url=https://www.gnu.org/software/make/ @purpose=Task runner @prefix=>= make = "4.4.1" # default version on macos is too old nodejs = "24.16.0" +# @name=pre-commit @url=https://pre-commit.com/ @purpose=Git hook framework pre-commit = "4.6.0" -python = "3.12" +python = "3.14" +# @name=shellcheck @url=https://www.shellcheck.net/ @purpose=Shell script linter shellcheck = "0.11.0" +# @name=Terraform @url=https://www.terraform.io/ @purpose=Infrastructure as code @prefix=>= terraform = "1.13.5" +# @name=terraform-docs @url=https://terraform-docs.io/ @purpose=Auto-generate module documentation terraform-docs = "0.24.0" -tflint = "0.62.1" -vale = "3.6.0" +# @name=tflint @url=https://github.com/terraform-linters/tflint @purpose=Terraform linter +tflint = "0.63.1" +# @name=Vale @url=https://vale.sh/ @purpose=English prose linter +vale = "3.15.1" +# @name=yq @url=https://github.com/mikefarah/yq @purpose=YAML processor (Go implementation) yq = "4.53.3" # Go-based tools (backend syntax requires quoting) +# @name=terraform-config-inspect @url=https://github.com/hashicorp/terraform-config-inspect @purpose=Generate aliased providers for validation "go:github.com/hashicorp/terraform-config-inspect" = "latest" # Referenced tasks (used by current automation/documentation) diff --git a/scripts/mise/update-tool-versions.sh b/scripts/mise/update-tool-versions.sh index 448e44e4..c1555535 100755 --- a/scripts/mise/update-tool-versions.sh +++ b/scripts/mise/update-tool-versions.sh @@ -140,7 +140,12 @@ if [[ "$UPGRADE_LEVEL" == "all" ]]; then mise upgrade --local --bump else outdated_json="$(mise outdated --local --bump --json)" - mapfile -t filtered_upgrade_rows < <(collect_filtered_upgrades_tsv "$UPGRADE_LEVEL" "$outdated_json") + + filtered_upgrade_rows=() + while IFS= read -r row; do + [[ -n "$row" ]] && filtered_upgrade_rows+=("$row") + done < <(collect_filtered_upgrades_tsv "$UPGRADE_LEVEL" "$outdated_json") + tools_to_upgrade=() for row in "${filtered_upgrade_rows[@]}"; do @@ -185,13 +190,14 @@ sync_tool_versions_from_toml() { exit 1 fi - declare -gA toml_versions=() + # Use a temp file as a portable key-value store (tab-separated) + TOML_VERSIONS_FILE="$(mktemp)" + TOML_METADATA_FILE="$(mktemp)" local in_tools=false local line key value local quoted_key_re='^[[:space:]]*"([^"]+)"[[:space:]]*=[[:space:]]*"([^"]+)"' local plain_key_re='^[[:space:]]*([A-Za-z0-9._:-]+)[[:space:]]*=[[:space:]]*"([^"]+)"' - - toml_versions=() + local pending_meta="" while IFS= read -r line; do if [[ "$line" =~ ^\[tools\]$ ]]; then @@ -207,6 +213,13 @@ sync_tool_versions_from_toml() { continue fi + # Capture @name/@url/@purpose/@prefix annotations from comments + if [[ "$line" =~ ^[[:space:]]*#.*@name= ]]; then + pending_meta="$line" + continue + fi + + # Skip plain comments without annotations if [[ "$line" =~ ^[[:space:]]*# ]]; then continue fi @@ -214,14 +227,22 @@ sync_tool_versions_from_toml() { if [[ $line =~ $quoted_key_re ]]; then key="${BASH_REMATCH[1]}" value="${BASH_REMATCH[2]}" - toml_versions["$key"]="$value" + printf '%s\t%s\n' "$key" "$value" >> "$TOML_VERSIONS_FILE" + if [[ -n "$pending_meta" ]]; then + printf '%s\t%s\n' "$key" "$pending_meta" >> "$TOML_METADATA_FILE" + fi + pending_meta="" continue fi if [[ $line =~ $plain_key_re ]]; then key="${BASH_REMATCH[1]}" value="${BASH_REMATCH[2]}" - toml_versions["$key"]="$value" + printf '%s\t%s\n' "$key" "$value" >> "$TOML_VERSIONS_FILE" + if [[ -n "$pending_meta" ]]; then + printf '%s\t%s\n' "$key" "$pending_meta" >> "$TOML_METADATA_FILE" + fi + pending_meta="" fi done < "$toml_file" @@ -234,8 +255,9 @@ sync_tool_versions_from_toml() { fi key="${line%%[[:space:]]*}" - if [[ -n "${toml_versions[$key]:-}" ]]; then - printf '%s %s\n' "$key" "${toml_versions[$key]}" >> "$tmp_file" + value="$(toml_version_of "$key")" + if [[ -n "$value" ]]; then + printf '%s %s\n' "$key" "$value" >> "$tmp_file" else printf '%s\n' "$line" >> "$tmp_file" fi @@ -244,6 +266,53 @@ sync_tool_versions_from_toml() { mv "$tmp_file" "$asdf_file" } +# Look up a tool version from the parsed TOML key-value file. +toml_version_of() { + local needle="$1" + grep -F "$(printf '%s\t' "$needle")" "$TOML_VERSIONS_FILE" 2>/dev/null | head -1 | cut -f2 +} + +toml_tool_keys() { + cut -f1 "$TOML_VERSIONS_FILE" | sort -f +} + +# Look up metadata from structured comments parsed during TOML reading. +# Returns: display_name|url|purpose|version_prefix (pipe-separated) +# Falls back to the tool key name with empty fields if no entry exists. +lookup_tool_metadata() { + local tool_key="$1" + local meta_line meta_comment + local name="" url="" purpose="" prefix="" + + meta_line="$(grep -F "$(printf '%s\t' "$tool_key")" "$TOML_METADATA_FILE" 2>/dev/null | head -1)" + if [[ -n "$meta_line" ]]; then + meta_comment="$(printf '%s' "$meta_line" | cut -f2-)" + + # Extract @key=value fields (value runs until next @key= or end of line) + if [[ "$meta_comment" =~ @name=([^@]*) ]]; then + name="${BASH_REMATCH[1]}" + name="${name%"${name##*[![:space:]]}"}" + fi + if [[ "$meta_comment" =~ @url=([^@]*) ]]; then + url="${BASH_REMATCH[1]}" + url="${url%"${url##*[![:space:]]}"}" + fi + if [[ "$meta_comment" =~ @purpose=([^@]*) ]]; then + purpose="${BASH_REMATCH[1]}" + purpose="${purpose%"${purpose##*[![:space:]]}"}" + fi + if [[ "$meta_comment" =~ @prefix=([^@]*) ]]; then + prefix="${BASH_REMATCH[1]}" + fi + fi + + # Fallback: use tool key as display name + [[ -z "$name" ]] && name="$tool_key" + [[ -z "$purpose" ]] && purpose="Tool managed by mise" + + printf '%s|%s|%s|%s' "$name" "$url" "$purpose" "$prefix" +} + sync_readme_prerequisites_from_toml() { local readme_file="README.md" local tmp_file @@ -255,39 +324,27 @@ sync_readme_prerequisites_from_toml() { exit 1 fi - # Tool metadata registry: tool_key -> "display_name|url|purpose|version_prefix" - # Only tools listed here appear in the generated prerequisites table. - # Add new entries when introducing a tool that developers must install. - declare -A tool_metadata=( - [actionlint]="actionlint|https://github.com/rhysd/actionlint|GitHub Actions linter|" - [bats]="bats-core|https://github.com/bats-core/bats-core|Bash test framework|" - [gitleaks]="Gitleaks|https://github.com/gitleaks/gitleaks|Secret scanning|" - [go:github.com/hashicorp/terraform-config-inspect]="terraform-config-inspect|https://github.com/hashicorp/terraform-config-inspect|Generate aliased providers for validation|" - [jq]="jq|https://jqlang.github.io/jq/|JSON processor|" - [make]="GNU make|https://www.gnu.org/software/make/|Task runner|>= " - [pre-commit]="pre-commit|https://pre-commit.com/|Git hook framework|" - [shellcheck]="shellcheck|https://www.shellcheck.net/|Shell script linter|" - [terraform]="Terraform|https://www.terraform.io/|Infrastructure as code|>= " - [terraform-docs]="terraform-docs|https://terraform-docs.io/|Auto-generate module documentation|" - [tflint]="tflint|https://github.com/terraform-linters/tflint|Terraform linter|" - [vale]="Vale|https://vale.sh/|English prose linter|" - [yq]="yq|https://github.com/mikefarah/yq|YAML processor (Go implementation)|" - ) - - # Build the table rows for tools present in both metadata and toml_versions - local rows="" - local key display_name url purpose version_prefix version - for key in "${!tool_metadata[@]}"; do - if [[ -z "${toml_versions[$key]:-}" ]]; then - continue + # Build the table rows for every parsed tool key, applying metadata + # from structured comments in mise.toml, with a generic fallback otherwise. + local rows_file + rows_file="$(mktemp)" + local key display_name url purpose version_prefix version tool_cell sort_key + while IFS= read -r key; do + [[ -z "$key" ]] && continue + version="$(toml_version_of "$key")" + [[ -z "$version" ]] && continue + + IFS='|' read -r display_name url purpose version_prefix <<< "$(lookup_tool_metadata "$key")" + + if [[ -n "$url" ]]; then + tool_cell="[$display_name]($url)" + else + tool_cell="$display_name" fi - IFS='|' read -r display_name url purpose version_prefix <<< "${tool_metadata[$key]}" - version="${version_prefix}${toml_versions[$key]}" - rows+="| [$display_name]($url) | $version | $purpose |"$'\n' - done - # Sort rows alphabetically by display name (first column) - rows="$(printf '%s' "$rows" | sort -t'[' -k2,2 -f)" + sort_key="$(printf '%s' "$display_name" | tr '[:upper:]' '[:lower:]')" + printf '%s\t| %s | %s%s | %s |\n' "$sort_key" "$tool_cell" "$version_prefix" "$version" "$purpose" >> "$rows_file" + done < <(toml_tool_keys) tmp_file="$(mktemp)" @@ -297,7 +354,7 @@ sync_readme_prerequisites_from_toml() { printf '%s\n' "$line" >> "$tmp_file" printf '%s\n' "| Tool | Version | Purpose |" >> "$tmp_file" printf '%s\n' "| --- | --- | --- |" >> "$tmp_file" - printf '%s\n' "$rows" >> "$tmp_file" + cut -f2- "$rows_file" >> "$tmp_file" continue fi @@ -316,6 +373,7 @@ sync_readme_prerequisites_from_toml() { done < "$readme_file" mv "$tmp_file" "$readme_file" + rm -f "$rows_file" } sync_tool_versions_from_toml diff --git a/scripts/terraform/upgrade-module.sh b/scripts/terraform/upgrade-module.sh index 195c6c4d..24833ab7 100755 --- a/scripts/terraform/upgrade-module.sh +++ b/scripts/terraform/upgrade-module.sh @@ -172,7 +172,10 @@ main() { if [[ "$1" == "update-all" ]]; then confirm_update_all - mapfile -t module_paths < <(discover_module_paths) + module_paths=() + while IFS= read -r _path; do + module_paths+=("$_path") + done < <(discover_module_paths) if [[ ${#module_paths[@]} -eq 0 ]]; then echo "ERROR: no Terraform modules were found under infrastructure/modules." >&2 diff --git a/tests/test-mise-task-surface.bats b/tests/test-mise-task-surface.bats index 8497f4a6..3a7c28e1 100644 --- a/tests/test-mise-task-surface.bats +++ b/tests/test-mise-task-surface.bats @@ -21,13 +21,22 @@ setup() { ) # Discover active tasks (uncommented [tasks.xxx] blocks) - mapfile -t ACTIVE_TASKS < <(grep -E '^\[tasks\.[^]]+\]$' "$MISE_FILE" | sed -E 's/^\[tasks\.([^]]+)\]$/\1/' | sort -u) + ACTIVE_TASKS=() + while IFS= read -r _line; do + ACTIVE_TASKS+=("$_line") + done < <(grep -E '^\[tasks\.[^]]+\]$' "$MISE_FILE" | sed -E 's/^\[tasks\.([^]]+)\]$/\1/' | sort -u) # Discover commented-out tasks (# [tasks.xxx] blocks) - mapfile -t COMMENTED_TASKS < <(grep -E '^# \[tasks\.[^]]+\]$' "$MISE_FILE" | sed -E 's/^# \[tasks\.([^]]+)\]$/\1/' | sort -u) + COMMENTED_TASKS=() + while IFS= read -r _line; do + COMMENTED_TASKS+=("$_line") + done < <(grep -E '^# \[tasks\.[^]]+\]$' "$MISE_FILE" | sed -E 's/^# \[tasks\.([^]]+)\]$/\1/' | sort -u) # Discover all referenced tasks across automation, docs, and tests - mapfile -t REFERENCED_TASKS < <( + REFERENCED_TASKS=() + while IFS= read -r _line; do + [[ -n "$_line" ]] && REFERENCED_TASKS+=("$_line") + done < <( grep -rEhn 'mise run [A-Za-z0-9._-]+' \ .github/workflows \ .github/actions \ diff --git a/tests/test-mise-task-surface.sh b/tests/test-mise-task-surface.sh index 61fb3300..3980a706 100755 --- a/tests/test-mise-task-surface.sh +++ b/tests/test-mise-task-surface.sh @@ -56,15 +56,25 @@ if [[ ! -f "$MiseFile" ]]; then exit 1 fi -mapfile -t active_tasks < <(grep -E '^\[tasks\.[^]]+\]$' "$MiseFile" | sed -E 's/^\[tasks\.([^]]+)\]$/\1/' | sort -u) -mapfile -t commented_tasks < <(grep -E '^# \[tasks\.[^]]+\]$' "$MiseFile" | sed -E 's/^# \[tasks\.([^]]+)\]$/\1/' | sort -u) +active_tasks=() +while IFS= read -r _line; do + active_tasks+=("$_line") +done < <(grep -E '^\[tasks\.[^]]+\]$' "$MiseFile" | sed -E 's/^\[tasks\.([^]]+)\]$/\1/' | sort -u) + +commented_tasks=() +while IFS= read -r _line; do + commented_tasks+=("$_line") +done < <(grep -E '^# \[tasks\.[^]]+\]$' "$MiseFile" | sed -E 's/^# \[tasks\.([^]]+)\]$/\1/' | sort -u) if [[ "${#active_tasks[@]}" -eq 0 ]]; then echo "No active tasks were found in $MiseFile" >&2 exit 1 fi -mapfile -t referenced_tasks < <( +referenced_tasks=() +while IFS= read -r _line; do + [[ -n "$_line" ]] && referenced_tasks+=("$_line") +done < <( grep -rEhn 'mise run [A-Za-z0-9._-]+' \ .github/workflows \ .github/actions \ diff --git a/tests/test-tool-version-upgrade.bats b/tests/test-tool-version-upgrade.bats index ebd8cddc..75084803 100644 --- a/tests/test-tool-version-upgrade.bats +++ b/tests/test-tool-version-upgrade.bats @@ -91,6 +91,7 @@ node = "20.11.0" pre-commit = "4.6.0" python = "3.12.0" shellcheck = "0.11.0" +# @name=Terraform @url=https://www.terraform.io/ @purpose=Infrastructure as code @prefix=>= terraform = "1.13.2" terraform-docs = "0.24.0" tflint = "0.62.1" @@ -186,6 +187,16 @@ run_helper() { assert_file_contains "$FIXTURE_ROOT/mise.lock" "# regenerated lock" } +@test "default: README prerequisites include generic fallback tool rows" { + run_helper + assert_file_contains "$FIXTURE_ROOT/README.md" "| node | 21.1.0 | Tool managed by mise |" +} + +@test "default: README prerequisites include known metadata overrides" { + run_helper + assert_file_contains "$FIXTURE_ROOT/README.md" "| [Terraform](https://www.terraform.io/) | >= 1.13.3 | Infrastructure as code |" +} + # --- Dry-run --- @test "dry-run: calls mise outdated only" { diff --git a/tests/test-tool-version-upgrade.sh b/tests/test-tool-version-upgrade.sh index 98c5edef..2722184c 100755 --- a/tests/test-tool-version-upgrade.sh +++ b/tests/test-tool-version-upgrade.sh @@ -185,6 +185,8 @@ assert_contains_file "$fixture_root/.tool-versions" "node 21.1.0" "Syncs node ve assert_contains_file "$fixture_root/.tool-versions" "go:github.com/hashicorp/terraform-config-inspect latest" "Preserves tool aliases from mise.toml" assert_not_contains_file "$fixture_root/.tool-versions" "terraform 1.13.2" "Removes stale terraform version from .tool-versions" assert_contains_file "$fixture_root/mise.lock" "# regenerated lock" "Rewrites mise.lock" +assert_contains_file "$fixture_root/README.md" "| node | 21.1.0 | Tool managed by mise |" "README includes generic fallback tool rows" +assert_contains_file "$fixture_root/README.md" "| [Terraform](https://www.terraform.io/) | >= 1.13.3 | Infrastructure as code |" "README includes known metadata overrides" echo printf '%s\n' "" > "$log_file"