From 4aa4902bf3a825d625d0afd982d2f7e2c07c455d Mon Sep 17 00:00:00 2001 From: John Stairs Date: Tue, 19 May 2026 15:40:23 +0000 Subject: [PATCH 1/4] Add access control bootstrap instructions. --- docs/.vitepress/config.mts | 8 +- .../installation/cloud-installation.md | 10 + docs/reference/access-control-bootstrap.md | 282 ++++++++++++++++++ 3 files changed, 298 insertions(+), 2 deletions(-) create mode 100644 docs/reference/access-control-bootstrap.md diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index d2d8c0cd..7abb89af 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -44,13 +44,17 @@ export default defineConfig({ collapsed: true, items: [ { - text: "Gadgetron examples", - link: "/reference/gadgetron/gadgetron", + text: "Bootstrapping access control", + link: "/reference/access-control-bootstrap", }, { text: "Database management", link: "/reference/database-management", }, + { + text: "Gadgetron examples", + link: "/reference/gadgetron/gadgetron", + }, { text: "tyger-proxy", link: "/reference/tyger-proxy" }, ], }, diff --git a/docs/introduction/installation/cloud-installation.md b/docs/introduction/installation/cloud-installation.md index 9b882fd0..e533896a 100644 --- a/docs/introduction/installation/cloud-installation.md +++ b/docs/introduction/installation/cloud-installation.md @@ -327,6 +327,16 @@ the config file and running: tyger access-control apply -f config.yml ``` +::: info Note +If you do not have permission to create app registrations in your Entra ID +tenant, or if your organization will not allow running `tyger +access-control apply` directly against the directory, see +[Bootstrapping access control with an Entra ID +admin](../../reference/access-control-bootstrap.md) for a one-time `az`-only +bootstrap that an Entra admin can run on your behalf. After that bootstrap, +the rest of this section applies as written. +::: + The part of the config file to edit is under the path `organizations[*].api.accessControl`. The first part of that section is parameters for authentication: diff --git a/docs/reference/access-control-bootstrap.md b/docs/reference/access-control-bootstrap.md new file mode 100644 index 00000000..869551d2 --- /dev/null +++ b/docs/reference/access-control-bootstrap.md @@ -0,0 +1,282 @@ +# Bootstrapping access control with an Entra ID admin + +The [`tyger access-control apply`](../introduction/installation/cloud-installation.md#set-up-access-control) +command is a convenience that creates and maintains the two Microsoft Entra ID +app registrations that Tyger uses for authentication (one for the API server and +one for the CLI), and that keeps the user/group role assignments on them in +sync. + +In some organizations, the people who will operate Tyger day-to-day do not have +the directory permissions required to create app registrations, and the Entra ID +admins who do have those permissions are not willing to run a third-party tool +such as `tyger` against the directory. This page describes how to do the initial +bootstrap using only `az` commands an Entra admin can review, after which the +Tyger operators ("owners") can take over and run `tyger access-control apply` +themselves for all subsequent changes. + +## What this bootstrap does + +The script below performs the minimum set of steps that require Entra admin +privileges: + +1. Creates the API app registration and its service principal. +2. Creates the CLI app registration and its service principal. +3. Adds the designated Tyger owners as **app owners** on both registrations. + +Once an account is listed as an app owner, it can update the application object +(define app roles, OAuth2 scopes, pre-authorized clients, redirect URIs, etc.) +and grant role assignments on the API app's service principal. Everything else +that `tyger access-control apply` normally does is then performed by the Tyger +owner under their own identity — no further Entra admin involvement is required. + +## Step 1 — Collect the Tyger owners' object IDs + +Designate at least one Tyger owner. These are the people who will subsequently +run `tyger access-control apply` to manage the app registrations and role +assignments. + +Each owner must be an individual user account — Entra ID app owners cannot be +groups or service principals. + +Each owner can find their own Entra ID object ID by running: + +```bash +az ad user show --id "$(az account show --query user.name -o tsv)" --query id -o tsv +``` + +Collect the object IDs of all the intended owners and pass them to the Entra +admin. + +## Step 2 — Have the Entra admin run one of the bootstrap scripts + +The admin should run **exactly one** of the two scripts below. They both produce +the same end state — two app registrations, two service principals, and the +Tyger owners listed as app owners on both — and both print the values the +Tyger owner needs in Step 3. They differ only in the form of the +[application ID URI](https://learn.microsoft.com/entra/identity-platform/security-best-practices-for-app-registration#application-id-uri) +they set on each app: + +- **Script A** uses `api://{tenantId}/tyger-server` and + `api://{tenantId}/tyger-cli`. These URIs are more readable than the app-ID + form and are accepted by Entra by default. If your tenant has a + [verified domain](https://learn.microsoft.com/entra/identity/users/domains-manage) + and you would prefer an even friendlier URI such as + `api://tyger.contoso.com/tyger-server`, edit the `api_app_uri` and + `cli_app_uri` variables before running the script. +- **Script B** uses `api://{appId}` — each app's own client ID — as the + identifier URI. The URI is less meaningful to a human reader, but this form + is always accepted regardless of tenant policy. Use this variant if Script A + is rejected (some tenants restrict identifier URIs to verified-domain hosts + only). + +In both scripts, fill in: + +- `owner_object_ids` — the object IDs collected in Step 1. +- `api_app_display_name` and `cli_app_display_name` — friendly names shown in + the Entra admin portal. + +Both scripts are idempotent in the sense that re-running them after a +successful bootstrap will produce errors for the already-existing resources +but will not corrupt anything. The admin only needs to run their chosen +script once. + +### Script A — tenant-ID (or verified-domain) URI + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# One or more object IDs of the Tyger owners. These accounts will be added as +# app owners on both app registrations so they can subsequently run +# `tyger access-control apply` themselves. +owner_object_ids=( + "FILL-IN-OWNER-OBJECT-ID-1" + # "FILL-IN-OWNER-OBJECT-ID-2" +) + +# Identifier URIs. The default `api://{tenantId}/...` form is accepted in any +# tenant. If your tenant has a verified domain you may prefer something like +# `api://tyger.contoso.com/tyger-server` instead. +tenant_id="$(az account show --query tenantId -o tsv)" +api_app_uri="api://${tenant_id}/tyger-server" +cli_app_uri="api://${tenant_id}/tyger-cli" + +api_app_display_name="Tyger API" +cli_app_display_name="Tyger CLI" + +# --- API app --------------------------------------------------------------- +api_app_id="$(az ad app create \ + --display-name "$api_app_display_name" \ + --identifier-uris "$api_app_uri" \ + --requested-access-token-version 2 \ + --query appId -o tsv)" + +az ad sp create --id "$api_app_id" + +for owner_object_id in "${owner_object_ids[@]}"; do + az ad app owner add --id "$api_app_id" --owner-object-id "$owner_object_id" +done + +# --- CLI app --------------------------------------------------------------- +cli_app_id="$(az ad app create \ + --display-name "$cli_app_display_name" \ + --identifier-uris "$cli_app_uri" \ + --requested-access-token-version 2 \ + --query appId -o tsv)" + +az ad sp create --id "$cli_app_id" + +for owner_object_id in "${owner_object_ids[@]}"; do + az ad app owner add --id "$cli_app_id" --owner-object-id "$owner_object_id" +done + +echo +echo "Bootstrap complete." +echo " tenantId: ${tenant_id}" +echo " apiAppUri: ${api_app_uri}" +echo " cliAppUri: ${cli_app_uri}" +echo " ownerObjectIds:" +for owner_object_id in "${owner_object_ids[@]}"; do + echo " - ${owner_object_id}" +done +``` + +### Script B — `api://{appId}` fallback + +Use this variant only if Script A is rejected by tenant policy (typically with +an error such as *"Values of identifierUris property must use a verified +domain of the organization or its subdomain"*). + +```bash +#!/usr/bin/env bash +set -euo pipefail + +owner_object_ids=( + "FILL-IN-OWNER-OBJECT-ID-1" + # "FILL-IN-OWNER-OBJECT-ID-2" +) + +api_app_display_name="Tyger API" +cli_app_display_name="Tyger CLI" + +tenant_id="$(az account show --query tenantId -o tsv)" + +# --- API app --------------------------------------------------------------- +api_app_id="$(az ad app create \ + --display-name "$api_app_display_name" \ + --requested-access-token-version 2 \ + --query appId -o tsv)" + +api_app_uri="api://${api_app_id}" +az ad app update --id "$api_app_id" --identifier-uris "$api_app_uri" +az ad sp create --id "$api_app_id" + +for owner_object_id in "${owner_object_ids[@]}"; do + az ad app owner add --id "$api_app_id" --owner-object-id "$owner_object_id" +done + +# --- CLI app --------------------------------------------------------------- +cli_app_id="$(az ad app create \ + --display-name "$cli_app_display_name" \ + --requested-access-token-version 2 \ + --query appId -o tsv)" + +cli_app_uri="api://${cli_app_id}" +az ad app update --id "$cli_app_id" --identifier-uris "$cli_app_uri" +az ad sp create --id "$cli_app_id" + +for owner_object_id in "${owner_object_ids[@]}"; do + az ad app owner add --id "$cli_app_id" --owner-object-id "$owner_object_id" +done + +echo +echo "Bootstrap complete." +echo " tenantId: ${tenant_id}" +echo " apiAppUri: ${api_app_uri}" +echo " cliAppUri: ${cli_app_uri}" +echo " ownerObjectIds:" +for owner_object_id in "${owner_object_ids[@]}"; do + echo " - ${owner_object_id}" +done +``` + +### Reporting back + +Whichever script was used, ask the admin to send back the values printed at +the end (`tenantId`, `apiAppUri`, `cliAppUri`, and the owner object IDs) along +with confirmation that the script completed successfully. + +## Step 3 — Owner takes over with `tyger access-control apply` + +Once the bootstrap is complete, one of the Tyger owners edits the main Tyger +[cloud configuration file](../introduction/installation/cloud-installation.md#generate-an-installation-configuration-file) +(`config.yml`) and fills in the `accessControl` section under the relevant +organization, at the path `organizations[*].api.accessControl`: + +```yaml +api: + accessControl: + tenantId: 72f988bf-86f1-41af-91ab-2d7cd011db47 # reported by the admin + apiAppUri: api://72f988bf-…/tyger-server # reported by the admin + cliAppUri: api://72f988bf-…/tyger-cli # reported by the admin + + apiAppId: "" # `tyger access-control apply` will fill in this value + cliAppId: "" # `tyger access-control apply` will fill in this value + + roleAssignments: + owner: + # At minimum, list the Tyger owners here so they can use Tyger themselves. + # These are the owner object IDs reported by the admin. + - kind: User + objectId: 33333333-3333-3333-3333-333333333333 + + contributor: [] +``` + +The values to set are: + +- `tenantId`, `apiAppUri`, `cliAppUri` — reported by the admin at the end of + the bootstrap script. +- `apiAppId` / `cliAppId` — leave empty; `tyger access-control apply` will + fill them in from the app registrations. +- `roleAssignments.owner` — list each Tyger owner using the `objectId` value + reported by the admin. Using `objectId` avoids the ambiguity of the user + principal name, which does not always match a user's email address. +- `roleAssignments.contributor` — list any additional users, groups, or + service principals that should have the contributor role. See + [Set up access control](../introduction/installation/cloud-installation.md#set-up-access-control) + for the supported principal forms. + +Then apply the configuration: + +```bash +tyger access-control apply -f config.yml +``` + +(If the configuration file contains more than one organization, also pass +`--org ` to select the one to apply.) + +This is the command that fills in the rest of the app registration details: +defining the `owner` and `contributor` app roles, declaring the OAuth2 +permission scope, configuring the CLI app as a public client with a +`http://localhost` redirect URI, marking the CLI app as a pre-authorized +client of the API app, and creating the requested app role assignments on the +API app's service principal. + +From this point on, the Tyger owners can re-run `tyger access-control apply` +themselves whenever role assignments need to change — no further Entra admin +involvement is required. + +## What is *not* done by the bootstrap script + +For transparency when reviewing the script with the Entra admin, note that the +script intentionally does **not**: + +- Define any app roles, OAuth2 permission scopes, pre-authorized clients, or + redirect URIs. Those are configured later by the owner via + `tyger access-control apply`. +- Grant admin consent for any permissions. The CLI app only requests a + delegated scope on the API app (which is in the same tenant and pre-authorized + by `tyger access-control apply`), so admin consent is not required. +- Assign any users to Tyger roles. That is done later by the owner via + `roleAssignments` in the configuration file. From bca664b4f1313d0ec341e8dc1db9e43e303e79b5 Mon Sep 17 00:00:00 2001 From: John Stairs Date: Tue, 19 May 2026 15:50:50 +0000 Subject: [PATCH 2/4] Refinements --- docs/reference/access-control-bootstrap.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/reference/access-control-bootstrap.md b/docs/reference/access-control-bootstrap.md index 869551d2..85113e07 100644 --- a/docs/reference/access-control-bootstrap.md +++ b/docs/reference/access-control-bootstrap.md @@ -75,10 +75,10 @@ In both scripts, fill in: - `api_app_display_name` and `cli_app_display_name` — friendly names shown in the Entra admin portal. -Both scripts are idempotent in the sense that re-running them after a -successful bootstrap will produce errors for the already-existing resources -but will not corrupt anything. The admin only needs to run their chosen -script once. +The admin only needs to run their chosen script once. If a step fails, the +script halts immediately (so there is no risk of partial corruption); the +admin can investigate, clean up any partially-created resources, and either +re-run the script or complete the remaining steps manually. ### Script A — tenant-ID (or verified-domain) URI @@ -94,9 +94,9 @@ owner_object_ids=( # "FILL-IN-OWNER-OBJECT-ID-2" ) -# Identifier URIs. The default `api://{tenantId}/...` form is accepted in any -# tenant. If your tenant has a verified domain you may prefer something like -# `api://tyger.contoso.com/tyger-server` instead. +# Identifier URIs. The default `api://{tenantId}/...` form is human-readable +# and is accepted by Entra by default. If your tenant has a verified domain +# you may prefer `api://tyger.contoso.com/tyger-server` instead. tenant_id="$(az account show --query tenantId -o tsv)" api_app_uri="api://${tenant_id}/tyger-server" cli_app_uri="api://${tenant_id}/tyger-cli" @@ -216,9 +216,9 @@ organization, at the path `organizations[*].api.accessControl`: ```yaml api: accessControl: - tenantId: 72f988bf-86f1-41af-91ab-2d7cd011db47 # reported by the admin - apiAppUri: api://72f988bf-…/tyger-server # reported by the admin - cliAppUri: api://72f988bf-…/tyger-cli # reported by the admin + tenantId: c546d652-e328-4c56-9cc3-030af6b7b194 # reported by the admin + apiAppUri: api://c546d652-e328-4c56-9cc3-030af6b7b194/tyger-server # reported by the admin + cliAppUri: api://c546d652-e328-4c56-9cc3-030af6b7b194/tyger-cli # reported by the admin apiAppId: "" # `tyger access-control apply` will fill in this value cliAppId: "" # `tyger access-control apply` will fill in this value From 45eaa4e794277edb111b8f5a202a88dd4185bc44 Mon Sep 17 00:00:00 2001 From: John Stairs Date: Tue, 19 May 2026 16:16:01 +0000 Subject: [PATCH 3/4] Mak docs-only PRs faster --- .github/workflows/pr-ci.yml | 106 ++++++++++++++++++++++++++++++++---- 1 file changed, 96 insertions(+), 10 deletions(-) diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index c5e7e249..f0f1b0ac 100644 --- a/.github/workflows/pr-ci.yml +++ b/.github/workflows/pr-ci.yml @@ -21,6 +21,66 @@ env: TYGER_AUTH_METHOD: "github" jobs: + # Determine whether the PR touches anything outside of docs/. When a PR only changes + # files under docs/, we skip the full build/test/deploy pipeline. We do this via + # per-job `if:` gates (rather than `paths-ignore` on the workflow trigger) so that + # the jobs still report as skipped and satisfy required status checks configured by + # job name in branch protection. The gating only applies to pull_request events; + # push and workflow_dispatch always run the full pipeline. + changes: + runs-on: ubuntu-latest + outputs: + non_docs: ${{ steps.filter.outputs.non_docs }} + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - id: filter + run: | + set -euo pipefail + + if [[ "${{ github.event_name }}" != "pull_request" ]]; then + # On push / workflow_dispatch, always run the full pipeline. + echo "non_docs=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + base_sha="${{ github.event.pull_request.base.sha }}" + head_sha="${{ github.event.pull_request.head.sha }}" + + # Make sure we have both commits available locally. + git fetch --no-tags --depth=1 origin "$base_sha" || true + + changed=$(git diff --name-only "$base_sha" "$head_sha") + echo "Changed files:" + echo "$changed" + + if echo "$changed" | grep -vE '^docs/' | grep -q .; then + echo "non_docs=true" >> "$GITHUB_OUTPUT" + else + echo "non_docs=false" >> "$GITHUB_OUTPUT" + fi + + build-docs: + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Build docs + run: | + set -euo pipefail + cd docs + npm install + npm run docs:build + # If this is running a Dependabot PR or PR from a fork, it we won't have access to secrets # nor will be be able to obtain a federated token to access Azure resources. # Therefore, in those cases, we use hosted runners with managed identity to access Azure resources. @@ -29,6 +89,9 @@ jobs: # Also, this repo is configured to require approvals for all workflowd external contributors, and we # should inspect the code in the PR before approving the run. test-azure-needs-hosted-runner: + needs: + - changes + if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ubuntu-latest outputs: AZURE_RUNS_ON_JSON: ${{ steps.set-vars.outputs.AZURE_RUNS_ON_JSON }} @@ -50,7 +113,9 @@ jobs: unit-tests-and-format: runs-on: ubuntu-latest needs: + - changes - get-config + if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' defaults: run: shell: bash @@ -117,17 +182,12 @@ jobs: exit 1 fi - - name: Build docs - run: | - set -euo pipefail - cd docs - npm install - npm run docs:build - build-images: needs: + - changes - test-azure-needs-hosted-runner - get-config + if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ${{ fromJson(needs.test-azure-needs-hosted-runner.outputs.AZURE_RUNS_ON_JSON)}} @@ -149,6 +209,9 @@ jobs: make -j 4 docker-build get-config: + needs: + - changes + if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ubuntu-latest defaults: run: @@ -217,7 +280,10 @@ jobs: publish-ghcr: runs-on: ubuntu-latest - needs: get-config + needs: + - changes + - get-config + if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' defaults: run: shell: bash @@ -246,8 +312,10 @@ jobs: build-binaries: runs-on: ubuntu-latest needs: + - changes - get-config - publish-ghcr + if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' defaults: run: shell: bash @@ -324,8 +392,10 @@ jobs: up: needs: + - changes - test-azure-needs-hosted-runner - get-config + if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ${{ fromJson(needs.test-azure-needs-hosted-runner.outputs.AZURE_RUNS_ON_JSON)}} defaults: run: @@ -363,9 +433,11 @@ jobs: restore-scale-to-zero: needs: + - changes - test-azure-needs-hosted-runner - up - get-config + if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ${{ fromJson(needs.test-azure-needs-hosted-runner.outputs.AZURE_RUNS_ON_JSON)}} defaults: run: @@ -394,9 +466,11 @@ jobs: integration-tests: name: integration-tests (${{ matrix.label }}) needs: + - changes - test-azure-needs-hosted-runner - get-config - up + if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ${{ fromJson(needs.test-azure-needs-hosted-runner.outputs.AZURE_RUNS_ON_JSON)}} env: TYGER_MIN_NODE_COUNT: "1" @@ -436,7 +510,9 @@ jobs: test-private-link-deployment: needs: + - changes - get-config + if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ["self-hosted", "1ES.Pool=tyger-gh-1es-westus2"] defaults: run: @@ -496,7 +572,9 @@ jobs: ensure-private-link-endpoints-not-public: needs: + - changes - test-private-link-deployment + if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ubuntu-latest defaults: run: @@ -548,10 +626,12 @@ jobs: windows-smoke-tests: needs: + - changes - test-azure-needs-hosted-runner - get-config - build-binaries - up + if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ${{ fromJson(needs.test-azure-needs-hosted-runner.outputs.AZURE_RUNS_ON_WINDOWS_JSON)}} steps: - name: Checkout @@ -595,8 +675,9 @@ jobs: verify-binaries: # Do not install from the unapproved GHCR registry for the long-running CI environment - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && needs.changes.outputs.non_docs == 'true' needs: + - changes - test-azure-needs-hosted-runner - get-config - publish-ghcr @@ -647,6 +728,9 @@ jobs: tyger api install -f <(./scripts/get-config.sh) verify-docker: + needs: + - changes + if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ubuntu-latest defaults: run: @@ -675,7 +759,9 @@ jobs: codeql: runs-on: ubuntu-latest - if: github.repository == 'microsoft/tyger' + needs: + - changes + if: github.repository == 'microsoft/tyger' && (github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true') defaults: run: shell: bash From f4cf59583ca039529f53ead9733f66404008e178 Mon Sep 17 00:00:00 2001 From: John Stairs Date: Tue, 19 May 2026 16:20:33 +0000 Subject: [PATCH 4/4] Revert "Mak docs-only PRs faster" This reverts commit 45eaa4e794277edb111b8f5a202a88dd4185bc44. --- .github/workflows/pr-ci.yml | 106 ++++-------------------------------- 1 file changed, 10 insertions(+), 96 deletions(-) diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index f0f1b0ac..c5e7e249 100644 --- a/.github/workflows/pr-ci.yml +++ b/.github/workflows/pr-ci.yml @@ -21,66 +21,6 @@ env: TYGER_AUTH_METHOD: "github" jobs: - # Determine whether the PR touches anything outside of docs/. When a PR only changes - # files under docs/, we skip the full build/test/deploy pipeline. We do this via - # per-job `if:` gates (rather than `paths-ignore` on the workflow trigger) so that - # the jobs still report as skipped and satisfy required status checks configured by - # job name in branch protection. The gating only applies to pull_request events; - # push and workflow_dispatch always run the full pipeline. - changes: - runs-on: ubuntu-latest - outputs: - non_docs: ${{ steps.filter.outputs.non_docs }} - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - id: filter - run: | - set -euo pipefail - - if [[ "${{ github.event_name }}" != "pull_request" ]]; then - # On push / workflow_dispatch, always run the full pipeline. - echo "non_docs=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - - base_sha="${{ github.event.pull_request.base.sha }}" - head_sha="${{ github.event.pull_request.head.sha }}" - - # Make sure we have both commits available locally. - git fetch --no-tags --depth=1 origin "$base_sha" || true - - changed=$(git diff --name-only "$base_sha" "$head_sha") - echo "Changed files:" - echo "$changed" - - if echo "$changed" | grep -vE '^docs/' | grep -q .; then - echo "non_docs=true" >> "$GITHUB_OUTPUT" - else - echo "non_docs=false" >> "$GITHUB_OUTPUT" - fi - - build-docs: - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Build docs - run: | - set -euo pipefail - cd docs - npm install - npm run docs:build - # If this is running a Dependabot PR or PR from a fork, it we won't have access to secrets # nor will be be able to obtain a federated token to access Azure resources. # Therefore, in those cases, we use hosted runners with managed identity to access Azure resources. @@ -89,9 +29,6 @@ jobs: # Also, this repo is configured to require approvals for all workflowd external contributors, and we # should inspect the code in the PR before approving the run. test-azure-needs-hosted-runner: - needs: - - changes - if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ubuntu-latest outputs: AZURE_RUNS_ON_JSON: ${{ steps.set-vars.outputs.AZURE_RUNS_ON_JSON }} @@ -113,9 +50,7 @@ jobs: unit-tests-and-format: runs-on: ubuntu-latest needs: - - changes - get-config - if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' defaults: run: shell: bash @@ -182,12 +117,17 @@ jobs: exit 1 fi + - name: Build docs + run: | + set -euo pipefail + cd docs + npm install + npm run docs:build + build-images: needs: - - changes - test-azure-needs-hosted-runner - get-config - if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ${{ fromJson(needs.test-azure-needs-hosted-runner.outputs.AZURE_RUNS_ON_JSON)}} @@ -209,9 +149,6 @@ jobs: make -j 4 docker-build get-config: - needs: - - changes - if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ubuntu-latest defaults: run: @@ -280,10 +217,7 @@ jobs: publish-ghcr: runs-on: ubuntu-latest - needs: - - changes - - get-config - if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' + needs: get-config defaults: run: shell: bash @@ -312,10 +246,8 @@ jobs: build-binaries: runs-on: ubuntu-latest needs: - - changes - get-config - publish-ghcr - if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' defaults: run: shell: bash @@ -392,10 +324,8 @@ jobs: up: needs: - - changes - test-azure-needs-hosted-runner - get-config - if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ${{ fromJson(needs.test-azure-needs-hosted-runner.outputs.AZURE_RUNS_ON_JSON)}} defaults: run: @@ -433,11 +363,9 @@ jobs: restore-scale-to-zero: needs: - - changes - test-azure-needs-hosted-runner - up - get-config - if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ${{ fromJson(needs.test-azure-needs-hosted-runner.outputs.AZURE_RUNS_ON_JSON)}} defaults: run: @@ -466,11 +394,9 @@ jobs: integration-tests: name: integration-tests (${{ matrix.label }}) needs: - - changes - test-azure-needs-hosted-runner - get-config - up - if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ${{ fromJson(needs.test-azure-needs-hosted-runner.outputs.AZURE_RUNS_ON_JSON)}} env: TYGER_MIN_NODE_COUNT: "1" @@ -510,9 +436,7 @@ jobs: test-private-link-deployment: needs: - - changes - get-config - if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ["self-hosted", "1ES.Pool=tyger-gh-1es-westus2"] defaults: run: @@ -572,9 +496,7 @@ jobs: ensure-private-link-endpoints-not-public: needs: - - changes - test-private-link-deployment - if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ubuntu-latest defaults: run: @@ -626,12 +548,10 @@ jobs: windows-smoke-tests: needs: - - changes - test-azure-needs-hosted-runner - get-config - build-binaries - up - if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ${{ fromJson(needs.test-azure-needs-hosted-runner.outputs.AZURE_RUNS_ON_WINDOWS_JSON)}} steps: - name: Checkout @@ -675,9 +595,8 @@ jobs: verify-binaries: # Do not install from the unapproved GHCR registry for the long-running CI environment - if: github.event_name == 'pull_request' && needs.changes.outputs.non_docs == 'true' + if: github.event_name == 'pull_request' needs: - - changes - test-azure-needs-hosted-runner - get-config - publish-ghcr @@ -728,9 +647,6 @@ jobs: tyger api install -f <(./scripts/get-config.sh) verify-docker: - needs: - - changes - if: github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true' runs-on: ubuntu-latest defaults: run: @@ -759,9 +675,7 @@ jobs: codeql: runs-on: ubuntu-latest - needs: - - changes - if: github.repository == 'microsoft/tyger' && (github.event_name != 'pull_request' || needs.changes.outputs.non_docs == 'true') + if: github.repository == 'microsoft/tyger' defaults: run: shell: bash