-
Notifications
You must be signed in to change notification settings - Fork 619
feat(spec render): Add supporting infrastructure for rendered specs #16674
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
christopherco
merged 3 commits into
microsoft:tomls/base/main
from
dmcilvaney:damcilva/4.0/add_render_pipeline
Apr 25, 2026
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
109 changes: 109 additions & 0 deletions
109
.github/instructions/pr-check-workflows.instructions.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| --- | ||
| applyTo: ".github/workflows/**" | ||
| description: ALWAYS review these instructions when reading or modifying PR check workflows, or any scripts referenced by the workflows. | ||
| --- | ||
|
|
||
| # PR Check Workflow Guidelines | ||
|
|
||
| ## Fork-PR-safe pattern: stub + reusable | ||
|
|
||
| Problem: `pull_request` triggers on fork PRs run without secrets and with a read-only token. `pull_request_target` runs with write access but checks out the BASE ref by default — easy to footgun into RCE if you then check out PR code with full privileges. | ||
|
|
||
| Pattern: | ||
|
|
||
| 1. **Stub workflow** on the default branch — triggered by `pull_request_target`, guards on repo owner, calls the reusable workflow. This is the only file GitHub will load from the base branch, so it locks the entrypoint. | ||
| 2. **Reusable workflow** (`workflow_call`) holds the real logic. Lives on the PR branch, so contributors can iterate on it. | ||
| 3. Stub passes `pull-requests: write` / `contents: read` only. Reusable declares its own minimum permissions. | ||
|
|
||
| Never check out PR code into a privileged job and then execute it on the host. Either: | ||
| - Run untrusted code inside a container with no secrets mounted, **or** | ||
| - Keep the privileged job read-only (lint, comment-post) and isolate code execution to a separate unprivileged job. | ||
|
|
||
| ## Prefer in-container for anything that executes PR code | ||
|
|
||
| If the check builds, renders, or runs PR code, do the whole thing inside the build container. Mock is a critical component of many azldev workflows. It requires many privileges to run successfully. It also is not available in Ubuntu, which is the default runner image for GitHub Actions. | ||
|
|
||
| - Mount the PR checkout read-only when possible; if writes are needed (e.g. `git add -N`), mount rw but don't leak host paths or secrets. | ||
| - Produce **all outputs** (reports, patches, diffs) inside the container and write them to a bind-mounted output dir. Host-side steps then only read these artifacts (json report, patch files, etc.) | ||
| - This eliminates a huge class of config-driven git RCE vectors (`core.fsmonitor`, `core.sshCommand`, hook files, etc.) because the host never runs git against PR-controlled config. | ||
|
|
||
| ### Container config | ||
|
|
||
| The shared runner image is [`.github/workflows/containers/azldev-runner.Dockerfile`](../workflows/containers/azldev-runner.Dockerfile). It's a minimal Azure Linux base with `mock`, `git`, `python3`, `sudo`, and `azldev` itself (installed to `/usr/local/bin` during image build) — enough to run any `azldev` subcommand. Reuse it rather than building a per-check image; add extras via a derived `FROM localhost/azldev-runner` stage if a check genuinely needs more. | ||
|
|
||
| `azldev` is baked in via `go install …/azldev@main` during image build. The pin lives in the Dockerfile so it can be reviewed and bumped deliberately. Image build context is `.github/workflows/containers/` only — keep it that way so the build can never see PR-controlled files. | ||
|
|
||
| Build it with the caller's UID so bind-mounted writes don't end up root-owned: | ||
|
|
||
| ```yaml | ||
| - name: Build azldev runner | ||
| run: | | ||
| docker build \ | ||
| --build-arg UID=$(id -u) \ | ||
| -t localhost/azldev-runner \ | ||
| -f .github/workflows/containers/azldev-runner.Dockerfile \ | ||
| .github/workflows/containers/ | ||
| ``` | ||
|
|
||
| #### Bind-mount conventions | ||
|
|
||
| | Mount | Mode | Purpose | | ||
| | ----- | ---- | ------- | | ||
| | `pr-head/` → `/workdir` | rw | PR checkout. rw because `azldev` writes to `specs/`, `base/build/`, etc. | | ||
| | `<host-output-dir>/` → `/output` | rw | Trusted-shape outputs (JSON reports, patches, ...) the container produces for the host to consume after the run. | | ||
| | `.github/workflows/scripts/` → `/scripts` | ro | Helper scripts from the trusted base checkout. | | ||
|
|
||
| #### Sandbox flags (minimum viable for `mock`) | ||
|
|
||
| ```yaml | ||
| docker run --rm \ | ||
| --cap-add=SYS_ADMIN \ | ||
| --security-opt seccomp=unconfined \ | ||
| --security-opt apparmor=unconfined \ | ||
| ... | ||
| ``` | ||
|
|
||
| Why each one is needed: | ||
|
|
||
| - **`--cap-add=SYS_ADMIN`** — `mock` sets up mount namespaces for its chroot. Without this you get `mount … exit status 32` during chroot init. | ||
| - **`--security-opt seccomp=unconfined`** — `mock` uses syscalls (`unshare`, `pivot_root`, etc.) that Docker's default seccomp profile blocks. | ||
| - **`--security-opt apparmor=unconfined`** — `ubuntu-latest` ships the `docker-default` AppArmor profile, which blocks `mount -t tmpfs` on paths under `/var/lib/mock` **even with `SYS_ADMIN` granted**. This is the confusing one; symptom is the same `exit status 32` after seccomp is already unconfined. | ||
|
|
||
| Avoid `--privileged` — it grants every capability and removes cgroup restrictions, which is a much bigger blast radius than the three flags above. | ||
|
|
||
| `--security-opt no-new-privileges` would be nice but `mock`'s `userhelper` needs setuid, which that flag blocks. | ||
|
|
||
| #### Running commands in the container | ||
|
|
||
| Use `bash -eu -o pipefail -c '…'` as the entrypoint invocation so a failure inside the heredoc actually fails the step: | ||
|
|
||
| ```yaml | ||
| localhost/azldev-runner \ | ||
| bash -eu -o pipefail -c ' | ||
| azldev component render -q -a --clean-stale -O json > /output/render.json | ||
| python3 /scripts/check_rendered_specs.py \ | ||
| --specs-dir "$(azldev config dump -q -f json | jq -r .project.renderedSpecsDir)" \ | ||
| --report /output/report.json \ | ||
| --patch /output/rendered-specs.patch | ||
| ' | ||
| ``` | ||
|
|
||
| Use single-quotes around the `-c` payload so host-side `${{ … }}` interpolation doesn't leak into the container script. If you need to pass a host value in, use `-e VAR=…` and reference `"$VAR"` inside — same script-injection concern as any other shell step. | ||
|
|
||
| ## Shell hardening in workflow steps | ||
|
|
||
| - Start every multi-line `run:` with `set -euo pipefail`. | ||
| - Quote **every** expansion involving a workflow input, matrix value, or file path: `"${VAR}"`, not `$VAR`. | ||
| - Never interpolate `${{ github.event.pull_request.* }}` directly into a shell script — assign to an `env:` var first, then reference as `"$VAR"`. Direct interpolation is a classic script-injection vector. | ||
| - For paths that must stay inside the repo, resolve with `realpath -m` and verify they start with the repo root prefix before use. | ||
|
|
||
| ## Markdown / HTML injection in PR comments | ||
|
|
||
| - Escape any PR-controlled string (file paths, error messages) before dropping into Markdown. | ||
| - Prefer code spans (`` `path` ``) or fenced blocks for anything path-like. | ||
|
|
||
| ## zizmor / pedantic linting | ||
|
|
||
| Workflows are linted with `zizmor --pedantic`. | ||
|
|
||
| Use `# zizmor: ignore[<rule>]` comments as an absolute last resort, and provide a comprehensive justification for why the rule is being ignored. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| --- | ||
| applyTo: "specs/**/*" | ||
| description: ALWAYS refer to this when working with rendered spec files (`*.spec`) in the `specs/` directory. | ||
| --- | ||
|
|
||
| # Rendered Spec Files (`specs/*.spec`) | ||
|
|
||
| ## What are rendered specs? | ||
|
|
||
| Rendered specs are generated by the `azldev comp render` command based on the component definitions and overlays. They are output to the `specs/` directory (as specified by `rendered-specs-dir` config) and should not be edited directly. | ||
|
|
||
| They are meant to be consumed by downstream processes (e.g., build pipelines) and are the source of truth for all subsequent steps. Any changes to the spec should be made via the component definition and overlays, not by editing the rendered spec. | ||
|
|
||
| ## Changing a rendered spec | ||
|
|
||
| To change a rendered spec, modify the component's `.comp.toml` and/or its overlays. Then re-run the render command to regenerate the spec: | ||
|
|
||
| ```bash | ||
| # VERY SLOW - Re-render all specs, removes any stale specs that are no longer defined in the components | ||
| azldev comp render -a --clean-stale -O json | ||
| ``` | ||
|
|
||
| ```bash | ||
| # Small set, will NOT remove stale specs, faster for iterative development | ||
| azldev comp render -p <component-1> -p <component-2> -O json | ||
| ``` | ||
|
|
||
| ```bash | ||
| # Custom output directory, useful for debugging. When not using the automatically configured spec directory, --force is | ||
| # required to delete and re-create the output folders if they already exist. | ||
| azldev comp render -p <component> -O json -o ./base/build/work/scratch/rendered-specs --force | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.