CI workflows
What runs without a Fabric tenant
Section titled “What runs without a Fabric tenant”Everything in CI runs without a Fabric tenant. Validation uses source files, generated CSVs, Python checks, and the site build. Power BI Desktop and Fabric service access are not required.
ci.yml
Section titled “ci.yml”# Tenant-free CI. Nothing here needs Fabric, Power BI, or any secret.name: CI
on: pull_request: push: branches: [main] workflow_dispatch:
permissions: contents: read
concurrency: group: ci-${{ github.ref }} cancel-in-progress: true
jobs: validate: name: Validate (${{ matrix.os }}) strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} timeout-minutes: 20 defaults: run: shell: pwsh steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.12"
# The same bootstrap a learner runs. It installs the hashed requirements and runs every # validation step (data, expected, questions, runs, tmdl, bindings, customizations, hygiene). - name: Initialize and validate run: ./scripts/Initialize-DevEnvironment.ps1
- name: pytest run: | $python = $IsWindows ? '.venv/Scripts/python.exe' : '.venv/bin/python' & $python -m pytest tools/python/tests if ($LASTEXITCODE) { exit $LASTEXITCODE }
- name: ruff if: runner.os == 'Linux' run: | .venv/bin/python -m ruff check tools/python if ($LASTEXITCODE) { exit $LASTEXITCODE } .venv/bin/python -m ruff format --check tools/python if ($LASTEXITCODE) { exit $LASTEXITCODE }
- name: Pester run: | $pester = Get-Module -ListAvailable Pester | Sort-Object Version -Descending | Select-Object -First 1 if (-not $pester -or $pester.Version -lt [version]'5.5.0') { Install-Module Pester -MinimumVersion 5.5.0 -Scope CurrentUser -Force -SkipPublisherCheck } Import-Module Pester -MinimumVersion 5.5.0 $config = New-PesterConfiguration $config.Run.Path = 'scripts/tests' $config.Run.Exit = $true $config.Output.Verbosity = 'Detailed' Invoke-Pester -Configuration $config
- name: PSScriptAnalyzer run: | if (-not (Get-Module -ListAvailable PSScriptAnalyzer)) { Install-Module PSScriptAnalyzer -Scope CurrentUser -Force } $findings = Invoke-ScriptAnalyzer -Path scripts -Recurse -Settings ./PSScriptAnalyzerSettings.psd1 $findings | Format-Table -AutoSize if ($findings) { exit 1 }
# Warns instead of failing until the maintainer has pushed the checkpoint tags. - name: Checkpoint tags if: runner.os == 'Linux' run: ./scripts/lab/Test-CheckpointTags.ps1 -AllowMissing
site: name: Lab site build runs-on: ubuntu-latest timeout-minutes: 15 defaults: run: working-directory: site steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "22" cache: npm cache-dependency-path: site/package-lock.json
- run: npm ci --no-audit --no-fund - run: npm run check - run: npm run build
dependency-review: name: Dependency review if: github.event_name == 'pull_request' runs-on: ubuntu-latest steps: - uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 with: fail-on-severity: moderateJobs:
| Job | Runs on | Purpose |
|---|---|---|
validate | Matrix: ubuntu-latest, windows-latest | Python install, validation, lab checks, conventions, hooks, and repo checks. |
site | ubuntu-latest | Installs site dependencies, runs content checks, and builds the Starlight site. |
dependency-review | ubuntu-latest, pull requests only | Reviews dependency changes. |
The workflow uses least-privilege permissions. The top-level workflow permission is contents: read.
SHA pinning
Section titled “SHA pinning”Actions are pinned by SHA with comments that show the human-readable version. This keeps automation stable and reviewable.
concurrency: group: ci-${{ github.ref }} cancel-in-progress: true
jobs: validate: name: Validate (${{ matrix.os }}) strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} timeout-minutes: 20 defaults: run: shell: pwsh steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.12"pages.yml
Section titled “pages.yml”# Publishes the self-paced lab site to GitHub Pages.# Runs only when the repository variable LAB_SITE_ENABLED is 'true', so copies made from the# template don't publish until their owner turns Pages on (Settings > Pages > Source: GitHub Actions).name: Lab site
on: push: branches: [main] paths: - "site/**" - "lab/**" - "evaluation/questions.yaml" - "rules/**" - ".github/agents/**" - ".github/prompts/**" - ".github/skills/**" - ".github/instructions/**" - ".github/workflows/pages.yml" workflow_dispatch:
permissions: contents: read
concurrency: group: pages cancel-in-progress: false
jobs: build: if: vars.LAB_SITE_ENABLED == 'true' runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false
- id: pages uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "22" cache: npm cache-dependency-path: site/package-lock.json
- name: Build working-directory: site env: SITE_ORIGIN: ${{ steps.pages.outputs.origin }} SITE_BASE: ${{ steps.pages.outputs.base_path }} SITE_REPO: ${{ github.repository }} run: | npm ci --no-audit --no-fund npm run check npm run build
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 with: path: site/dist
deploy: needs: build runs-on: ubuntu-latest permissions: pages: write id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - id: deployment uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1Jobs:
| Job | Purpose |
|---|---|
build | Builds the site and uploads the Pages artifact when LAB_SITE_ENABLED is true. |
deploy | Deploys to GitHub Pages using the artifact from build. |
One-time setup:
- Go to Settings > Pages.
- Set Source to GitHub Actions.
- Add repository variable
LAB_SITE_ENABLED=true.
If the variable is missing or false, Pages does not run.
copilot-setup-steps.yml
Section titled “copilot-setup-steps.yml”# Prepares the Copilot cloud agent's environment before it starts work on an assigned issue (Lab 13).# The job name must be copilot-setup-steps. See# https://docs.github.com/copilot/how-tos/use-copilot-agents/coding-agent/customize-the-agent-environmentname: Copilot setup steps
on: workflow_dispatch: push: paths: [".github/workflows/copilot-setup-steps.yml"] pull_request: paths: [".github/workflows/copilot-setup-steps.yml"]
permissions: contents: read
jobs: copilot-setup-steps: runs-on: ubuntu-latest timeout-minutes: 20 permissions: contents: read steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.12"
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "22"
- name: Python tools and validation shell: pwsh run: ./scripts/Initialize-DevEnvironment.ps1
# Installs the pinned Skills for Fabric skill from apm.yml into .agents/skills, where the # cloud agent discovers project skills. --only apm skips the MCP servers that package declares. - name: Skills for Fabric (APM) run: | python -m venv "$RUNNER_TEMP/apm" "$RUNNER_TEMP/apm/bin/pip" install --disable-pip-version-check --quiet apm-cli==0.31.0 "$RUNNER_TEMP/apm/bin/apm" install --only apm --target copilotThe job is named copilot-setup-steps. It prepares cloud agent sessions by installing Python dependencies and APM, then installing the pinned Skills for Fabric package.
Dependabot
Section titled “Dependabot”version: 2updates: - package-ecosystem: github-actions directory: / schedule: interval: weekly groups: actions: patterns: ["*"] - package-ecosystem: pip directory: /tools/python schedule: interval: weekly groups: python: patterns: ["*"] - package-ecosystem: npm directory: /site schedule: interval: weekly groups: site: patterns: ["*"]Dependabot watches GitHub Actions, site npm dependencies, and Python dependencies. Keep Dependabot pull requests small and let CI prove the lab still works.
Secret scanning and push protection
Section titled “Secret scanning and push protection”Enable GitHub secret scanning and push protection on the repository. The lab should never need committed tokens, tenant secrets, or connection strings. MCP values should come from inputs, environment variables, or GitHub MCP secrets.
Local checks
Section titled “Local checks”pwsh ./scripts/Invoke-Validation.ps1 -Testscd sitenpm run checknpm run buildpwsh ./scripts/Invoke-Validation.ps1 -Testscd sitenpm run checknpm run build