Skip to content
Open
Show file tree
Hide file tree
Changes from 75 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
ac2d118
Init PR checks
Mar 18, 2026
fc68715
PR check calls CT api
Mar 24, 2026
13bd561
Fixed minor format err
Apr 9, 2026
54deac1
fix: Removed group vars
Apr 10, 2026
77722f2
Set default container to azl3
Apr 10, 2026
884357e
Use azl agent pool
Apr 10, 2026
ebf57c3
Trying a Linux container instead of custom agent pool.
PawelWMS Apr 10, 2026
ab05a66
Removing 'addSpnToEnvironment: true' to hide sensitive data.
PawelWMS Apr 10, 2026
9b5125c
Passing 'API_TOKEN' through 'env'.
PawelWMS Apr 10, 2026
65ab31f
Security and refactoring tweaks.
PawelWMS Apr 10, 2026
59949c4
Trying to fix variable access.
PawelWMS Apr 10, 2026
7cb83df
Trying to enable network in OB.
PawelWMS Apr 10, 2026
bcac4d7
Adding 'azldev'.
PawelWMS Apr 10, 2026
4524da0
Using internal Go proxy.
PawelWMS Apr 11, 2026
7e35406
Fix command.
PawelWMS Apr 11, 2026
8e02390
Move failing stuff to the top.
PawelWMS Apr 11, 2026
3451bb6
Add SC check.
PawelWMS Apr 11, 2026
a51e774
Disabling rendering.
PawelWMS Apr 11, 2026
b45a6b8
Moving variables group around.
PawelWMS Apr 11, 2026
a1acfb5
Revert "Moving variables group around."
PawelWMS Apr 11, 2026
f6e2aaa
Hard-coding the SC.
PawelWMS Apr 11, 2026
e79f6e3
Removing unused argument.
PawelWMS Apr 13, 2026
4a8ae72
Added scaffolding for AI.
PawelWMS Apr 13, 2026
decc6c6
Clean-up + adding dummy delta components listing
PawelWMS Apr 13, 2026
c9c9495
Clean-up
PawelWMS Apr 13, 2026
d3fbbc4
Switching to the test branch.
PawelWMS Apr 13, 2026
4875caf
Fixing "trim"
PawelWMS Apr 13, 2026
48655d3
Adding build reason to CT call.
PawelWMS Apr 13, 2026
48e2b65
Making template NonOfficial for debugging.
PawelWMS Apr 13, 2026
b5abce9
Revert "Making template NonOfficial for debugging."
PawelWMS Apr 13, 2026
2a8d0d8
Always printing the service response.
PawelWMS Apr 13, 2026
eccbca2
Refactoring
PawelWMS Apr 13, 2026
0384f1f
Fixing the SC name.
PawelWMS Apr 13, 2026
b5bc996
Empty commit.
PawelWMS Apr 13, 2026
c3e6348
Moving the pipeline YAML.
PawelWMS Apr 14, 2026
933ce3d
Testing 'mock'.
PawelWMS Apr 14, 2026
f71e678
Refactoring.
PawelWMS Apr 15, 2026
298122b
Moving the pipeline YAML.
PawelWMS Apr 15, 2026
289c16b
Testing 'mock'.
PawelWMS Apr 15, 2026
36c30ff
Listing changed components.
PawelWMS Apr 15, 2026
ea27929
Testing 'mock'.
PawelWMS Apr 15, 2026
8eef5b7
Testing 'mock'.
PawelWMS Apr 15, 2026
eec5648
Full run
PawelWMS Apr 15, 2026
5f098f1
Fixing mock
PawelWMS Apr 15, 2026
53dcc26
Fixing mock
PawelWMS Apr 15, 2026
09f6aec
Trying to run on host.
PawelWMS Apr 15, 2026
d21fe10
Fixing mock
PawelWMS Apr 15, 2026
405c251
Fixing git config
PawelWMS Apr 15, 2026
73d3f0f
Fixing git config
PawelWMS Apr 15, 2026
fe1a2ec
Making component render quiet.
PawelWMS Apr 15, 2026
8485fc3
Forcing render overwrite.
PawelWMS Apr 15, 2026
3231fcf
Debugging with simple input
PawelWMS Apr 15, 2026
ccd745b
Dummy 'atlas' change - REVERT ME
PawelWMS Apr 15, 2026
e834162
Add logging.
PawelWMS Apr 15, 2026
39713b8
Switching to using a Python script for change detection.
PawelWMS Apr 16, 2026
cdd9929
Add logging.
PawelWMS Apr 16, 2026
ebde7f7
Add logging.
PawelWMS Apr 16, 2026
d5c6c69
Failing on updated specs.
PawelWMS Apr 16, 2026
f4ac9a6
Adding extra logging.
PawelWMS Apr 16, 2026
9f2b512
Revert "Dummy 'atlas' change - REVERT ME"
PawelWMS Apr 16, 2026
7c308d9
Going back to checking all components.
PawelWMS Apr 16, 2026
0ef9e56
Fix comments.
PawelWMS Apr 16, 2026
0c2bb12
Extend VS Code settings.
PawelWMS Apr 16, 2026
dd68939
Refactoring, clean-up.
PawelWMS Apr 16, 2026
81ab293
Moving CT call to a Python script.
PawelWMS Apr 17, 2026
4db3223
Checking with upstream pip
PawelWMS Apr 17, 2026
9d4d2ec
Re-naming the script
PawelWMS Apr 17, 2026
ef0b05d
Adding job status polling.
PawelWMS Apr 17, 2026
3bb83f6
Restoring 'PipAuthenticate'.
PawelWMS Apr 17, 2026
4e513e4
Restoring 'PipAuthenticate'.
PawelWMS Apr 17, 2026
3bd3d41
Debugging with simple input
PawelWMS Apr 18, 2026
af97a2e
Revert "Debugging with simple input"
PawelWMS Apr 18, 2026
c7774b0
Merge branch 'tomls/base/main' into asalinas/tomls/pr-check
PawelWMS Apr 20, 2026
f79a0f4
Initial ADO YAML instructions.
PawelWMS Apr 21, 2026
3b0efd6
Refactoring the ADO template into raw + wrapper.
PawelWMS Apr 21, 2026
cc7c643
Debugging with dummy components
PawelWMS Apr 21, 2026
f30d577
Disabling spec render check.
PawelWMS Apr 21, 2026
3571917
Supporting source branch or commit.
PawelWMS Apr 21, 2026
37cc40e
Disabling target commit.
PawelWMS Apr 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
302 changes: 302 additions & 0 deletions .github/instructions/ado-pipeline.instructions.md

Large diffs are not rendered by default.

63 changes: 63 additions & 0 deletions .github/workflows/ado/sources-upload.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Microsoft Corporation
#
# Wrapper pipeline — passed to ADO as the entry point. This file owns all
# OneBranch-specific wiring (governed templates repo, Official vs NonOfficial
# variant, featureFlags) and delegates the actual stages/jobs/steps to the
# raw stages template at:
# .github/workflows/ado/templates/sources-upload-stages.yml
#
# Authenticates via Workload Identity Federation (OIDC) and calls the Control
# Tower prcheck API with PR context.
#
# Prerequisites (ADO / Azure Portal):
# 1. Entra ID App Registration with audience URI
# "api://<ControlTower-ClientId>" (see variable group below).
# 2. Federated identity credential on the app registration for the ADO
# service connection (issuer: https://vstoken.dev.azure.com/<org-id>,
# subject: sc://<org>/<project>/<service-connection-name>).
# 3. ARM service connection in ADO project settings using Workload Identity
# Federation (manual).
# 4. ADO branch policy or pipeline PR trigger configured to fire on PRs.
#
# Variable Group (ADO Pipelines > Library):
# Name: "ControlTower-PRCheck"
# Required variables:
# - ApiAudience : Entra ID audience URI for the Control Tower app
# - ApiBaseUrl : Base URL of the Control Tower service
# - AzldevCommit : Commit hash for azldev (go install ...@<commit>)

# Trigger controlled by ADO branch policy — not YAML triggers.
trigger: none

pr: none

resources:
repositories:
- repository: templates
type: git
name: OneBranch.Pipelines/GovernedTemplates
ref: refs/heads/main

extends:
template: v2/OneBranch.Official.CrossPlat.yml@templates
parameters:
featureFlags:
golang:
internalModuleProxy:
enabled: true
LinuxHostVersion:
Network: R1
runOnHost: true
EnableCDPxPAT: false
stages:
- template: /.github/workflows/ado/templates/sources-upload-stages.yml@self
parameters:
outputDirectory: $(Build.ArtifactStagingDirectory)/output
artifactBaseName: prcheck
containerImage: mcr.microsoft.com/onebranch/azurelinux/build:3.0
poolType: linux
serviceConnection: CT-Endpoints-Access-ServiceConnection-DEV
Comment thread
PawelWMS marked this conversation as resolved.
variableGroup: ControlTower-PRCheck
# Must exceed the script's --poll-timeout-seconds (default 7200s = 120m)
# with enough headroom for setup steps and the final API call.
timeoutInMinutes: 150
181 changes: 181 additions & 0 deletions .github/workflows/ado/templates/sources-upload-stages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# Microsoft Corporation
#
# Raw stages template for the sources-upload PR check pipeline.
#
# This template is OneBranch-agnostic: it declares the stages/jobs/steps that
# do the actual work and exposes the OneBranch-coupled knobs as parameters.
# The wrapper at .github/workflows/ado/sources-upload.yml is responsible for
# choosing the OneBranch governed template variant (Official vs NonOfficial),
# configuring its featureFlags, and supplying concrete values for the
# parameters declared below.

parameters:
- name: outputDirectory
type: string
- name: artifactBaseName
type: string
- name: containerImage
type: string
- name: poolType
type: string
default: linux
- name: serviceConnection
type: string
- name: variableGroup
type: string
- name: timeoutInMinutes
type: number

stages:
- stage: PRCheck
jobs:
- job: CallControlTowerAPI
# Must exceed the script's --poll-timeout-seconds (default 7200s = 120m)
# with enough headroom for setup steps and the final API call.
timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
pool:
type: ${{ parameters.poolType }}
variables:
- group: ${{ parameters.variableGroup }}
- name: ob_outputDirectory
value: ${{ parameters.outputDirectory }}
- name: ob_artifactBaseName
value: ${{ parameters.artifactBaseName }}
- name: LinuxContainerImage
value: ${{ parameters.containerImage }}
steps:
- task: PipAuthenticate@1
displayName: "Authenticate pip"
inputs:
artifactFeeds: "azl/ControlTowerFeed"

- script: |
set -euo pipefail

echo "##[group]Mock"
tdnf install -y mock mock-rpmautospec python3-chardet
sudo usermod -aG mock "$(whoami)"
Comment thread
PawelWMS marked this conversation as resolved.
echo "##[endgroup]"

echo "##[group]Azldev"
echo "Installing azldev@${AZLDEV_COMMIT}..."
go install "github.com/microsoft/azure-linux-dev-tools/cmd/azldev@${AZLDEV_COMMIT}"

go_bin_path="$(go env GOPATH)/bin"
echo "##vso[task.prependpath]$go_bin_path"

"$go_bin_path/azldev" --version
echo "##[endgroup]"

echo "##[group]Python dependencies"
pip install -r .github/workflows/scripts/control-tower-prcheck/requirements.txt
echo "##[endgroup]"
displayName: "Install dependencies"
env:
AZLDEV_COMMIT: $(AzldevCommit)

- script: |
set -euo pipefail

# Workaround for an ADO git config error during spec rendering.
# The config key may not be present on every agent image, so tolerate its absence.
git config --unset extensions.worktreeConfig || true

# Full history is needed for spec rendering to work.
git fetch --unshallow
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

git fetch --unshallow exits non-zero when the checkout is already a full clone (“--unshallow on a complete repository…”), which would fail the job. Consider making this robust by detecting a shallow repo first (e.g., git rev-parse --is-shallow-repository) or falling back to a normal git fetch when --unshallow fails.

Suggested change
git fetch --unshallow
if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then
git fetch --unshallow
else
git fetch
fi

Copilot uses AI. Check for mistakes.

specs_dir="specs"

echo "Rendering specs..."
azldev component render -q -a -o "$specs_dir" --force
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably want --clean-stale here, or just use -a with the default config dir (ie don't use -o).
If its config driven, it does all the "right" things automatically.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I forgot about that. When I was writing this, the config wasn't there, yet. Or it wasn't read by azldev - I forgot which one it was.:)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I've got a solid flow for isolating mock if this ever needs to handle untrusted input: https://github.com/microsoft/azurelinux/pull/16674/changes#diff-30a9c2fec326355d71a476f939a68c942b218cd7479ea9710f7afd4c5d41695bR105

If we can guarantee this only runs on code we have looked at, might not care.


# Check for any new or modified files under the specs directory.
if ! python3 .github/workflows/scripts/check_rendered_specs.py --specs-dir "$specs_dir"; then
echo "##[group]Git diff"
git --no-pager diff -- "$specs_dir"
echo "##[endgroup]"
exit 1
fi
displayName: "Verify rendered specs are up to date"

- script: |
set -euo pipefail

# For both triggers, Build.SourceVersion is the relevant source commit:
# - PR trigger: ADO sets it to the GitHub-created merge commit (refs/pull/{n}/merge).
# - CI/merge-queue trigger: it is the commit that landed on the base branch.
source_hash="$SOURCE_COMMIT"

if [[ "$BUILD_REASON" == "PullRequest" ]]; then
# PR trigger: ADO provides the target branch directly.
target_hash=$(git ls-remote "$UPSTREAM_REPO_URL" "$PR_TARGET_BRANCH" | cut -f1)
else
# CI/merge-queue trigger: extract the base branch from the merge-queue branch name.
# Branch format: refs/heads/gh-readonly-queue/{base_branch}/pr-{pr_number}-{head_sha}
# Using 'test/' branches until ready with the merge queue, but the parsing logic is the same.
if ! base_branch=$(grep -oP '(?<=test/gh-readonly-queue/).+(?=/pr-[^/]+$)' <<< "$SOURCE_BRANCH"); then
echo "##[error]Unsupported SOURCE_BRANCH '$SOURCE_BRANCH' for non-PullRequest build. Expected 'refs/heads/test/gh-readonly-queue/<base>/pr-<n>-<sha>'."
Comment on lines +116 to +119
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inline comment says the merge-queue branch format is refs/heads/gh-readonly-queue/{base_branch}/pr-..., but the parsing regex only matches test/gh-readonly-queue/.... If the pipeline runs on the actual merge queue branch (without the test/ prefix), base_branch extraction will fail and block the pipeline. Consider supporting both prefixes (or matching on gh-readonly-queue/ regardless of an optional test/ prefix) and align the error message/comment with the accepted formats.

Suggested change
# Branch format: refs/heads/gh-readonly-queue/{base_branch}/pr-{pr_number}-{head_sha}
# Using 'test/' branches until ready with the merge queue, but the parsing logic is the same.
if ! base_branch=$(grep -oP '(?<=test/gh-readonly-queue/).+(?=/pr-[^/]+$)' <<< "$SOURCE_BRANCH"); then
echo "##[error]Unsupported SOURCE_BRANCH '$SOURCE_BRANCH' for non-PullRequest build. Expected 'refs/heads/test/gh-readonly-queue/<base>/pr-<n>-<sha>'."
# Supported branch formats:
# - refs/heads/gh-readonly-queue/{base_branch}/pr-{pr_number}-{head_sha}
# - refs/heads/test/gh-readonly-queue/{base_branch}/pr-{pr_number}-{head_sha}
if ! base_branch=$(grep -oP '(?<=/(?:test/)?gh-readonly-queue/).+(?=/pr-[^/]+$)' <<< "$SOURCE_BRANCH"); then
echo "##[error]Unsupported SOURCE_BRANCH '$SOURCE_BRANCH' for non-PullRequest build. Expected 'refs/heads/gh-readonly-queue/<base>/pr-<n>-<sha>' or 'refs/heads/test/gh-readonly-queue/<base>/pr-<n>-<sha>'."

Copilot uses AI. Check for mistakes.
exit 1
fi
target_hash=$(git ls-remote "$UPSTREAM_REPO_URL" "refs/heads/$base_branch" | cut -f1)
fi

echo "Source commit: $source_hash"
echo "Target commit: $target_hash"

echo "##vso[task.setvariable variable=sourceCommit]$source_hash"
echo "##vso[task.setvariable variable=targetCommit]$target_hash"
env:
BUILD_REASON: $(Build.Reason)
PR_TARGET_BRANCH: $(System.PullRequest.TargetBranch)
SOURCE_BRANCH: $(Build.SourceBranch)
SOURCE_COMMIT: $(Build.SourceVersion)
UPSTREAM_REPO_URL: $(Build.Repository.Uri)
displayName: "Determine source and target commit hashes"

- script: |
set -euo pipefail

cat << EOF
NOTE: It's possible more components changed between the source and target commits than displayed below.
This workflow only checks for updates in the 'sources' files and ignores all other changes.
EOF

components=()
while IFS= read -r line; do
components+=("$(basename "$(dirname "$line")")")
done < <(git diff "$SOURCE_COMMIT" "$TARGET_COMMIT" --name-only | grep '/sources$' | sort -u)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like ... might be more appropriate here, .. (or default) diffs ALL changes including those from other PRs. ... seems to auto-calculate the merge base for the PR.

Suggested change
done < <(git diff "$SOURCE_COMMIT" "$TARGET_COMMIT" --name-only | grep '/sources$' | sort -u)
done < <(git diff "$TARGET_COMMIT...$SOURCE_COMMIT" --name-only | grep '/sources$' | sort -u)


components="$(IFS=, ; echo "${components[*]}")"
echo "Affected components: $components"
echo "##vso[task.setvariable variable=components]$components"
env:
SOURCE_COMMIT: $(sourceCommit)
TARGET_COMMIT: $(targetCommit)
displayName: "Determine affected components"

- task: AzureCLI@2
displayName: "Call Control Tower 'prcheck' API"
inputs:
azureSubscription: ${{ parameters.serviceConnection }}
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
set -euo pipefail

python3 .github/workflows/scripts/control-tower-prcheck/run_control_tower_prcheck.py \
--api-audience "$API_AUDIENCE" \
--api-base-url "$API_BASE_URL" \
--build-reason "$BUILD_REASON" \
--components "$COMPONENTS" \
--source-commit "$SOURCE_COMMIT" \
--target-commit "$TARGET_COMMIT" \
--repo-uri "$UPSTREAM_REPO_URL"
env:
API_AUDIENCE: $(ApiAudience)
API_BASE_URL: $(ApiBaseUrl)
BUILD_REASON: $(Build.Reason)
COMPONENTS: $(components)
SOURCE_COMMIT: $(sourceCommit)
TARGET_COMMIT: $(targetCommit)
UPSTREAM_REPO_URL: $(Build.Repository.Uri)
10 changes: 5 additions & 5 deletions .github/workflows/scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ The easiest way to get quick, interactive feedback is with [GitHub Copilot](http
```bash
cd ~/repos/azurelinux
python -m venv .venv && source .venv/bin/activate
pip install -r .github/workflows/scripts/requirements.txt
pip install -r .github/workflows/scripts/spec-review/requirements.txt
gh copilot
# OR
npm install -g @github/copilot
Expand All @@ -56,8 +56,8 @@ copilot --version # verify CLI
--spec base/comps/azurelinux-repos/azurelinux-repos.spec

# Validate / inspect
python .github/workflows/scripts/spec_review_schema.py /tmp/spec_review_report.json --all
python .github/workflows/scripts/spec_review_schema.py /tmp/spec_review_report.json --json | jq
python .github/workflows/scripts/spec-review/spec_review_schema.py /tmp/spec_review_report.json --all
python .github/workflows/scripts/spec-review/spec_review_schema.py /tmp/spec_review_report.json --json | jq
```

The script supports additional options (like selecting a model, or using different URLs); run with `--help` for details.
Expand Down Expand Up @@ -119,7 +119,7 @@ copilot --agent spec-review
```bash
# For semi-automated or fully automated runs, use -i or -p flags:
prompt="Review: base/comps/azurelinux-release/azurelinux-release.spec against packaging guidelines.\n"\
"Write JSON to spec_review_report.json and validate with: python .github/workflows/scripts/spec_review_schema.py spec_review_report.json"
"Write JSON to spec_review_report.json and validate with: python .github/workflows/scripts/spec-review/spec_review_schema.py spec_review_report.json"

# Semi-interactive (runs prompt, then waits for the user)
copilot --agent spec-review \
Expand All @@ -144,7 +144,7 @@ copilot --agent spec-review \
-p "$prompt"

# Review output
python .github/workflows/scripts/spec_review_schema.py spec_review_report.json --all
python .github/workflows/scripts/spec-review/spec_review_schema.py spec_review_report.json --all
```

## Future work
Expand Down
Loading
Loading