Repository navigation
Conversation
The GitOps repository template written by `fleetctl generate-gitops` gains a pre-apply step, check-app-store-apps.sh, that asks the server which App Store apps the VPP account can supply and fails the workflow when a declared app_store_id is not among them. The error names the app, links its App Store page and lists the files that declare it. Fleet only rejects an unavailable App Store app during a real apply, and when a fleet is being added to the VPP list fleetctl re-applies every VPP fleet without App Store apps before adding them back, so a failure part-way through leaves the remaining fleets without their apps. Checking first turns that into a failed pull request. The script is bash, curl and jq like gitops.sh, uses the same FLEET_URL and FLEET_API_TOKEN, handles both the fleets/fleet_id and older teams/team_id vocabularies, pages through software titles, skips fleets without a VPP token and ignores Play Store entries. Wired into the GitHub composite action and the GitLab pipeline templates; the GitHub workflow's chmod step covers it. The generator test expects the new file. Companion to the server-side dry-run fix in fleetdm#54019. Related to fleetdm#54013. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
5 tasks done
jkatz01
pushed a commit
that referenced
this pull request
Oct 10, 2026
**Related issue:** #54013 `fleetctl gitops --dry-run` now fails when an `app_store_id` under `software.app_store_apps` is not available on the VPP account, with the same `requested app not available on vpp account` error the real apply returns. Before this change the dry run returned success before looking at Apple's asset list, so a wrong or unlicensed App Store ID passed CI and only failed during the real apply. When the failing app belonged to a fleet that was being added to the VPP list, fleetctl's two-pass re-apply had already cleared the other VPP fleets' App Store apps before it stopped, which is the incident behind #54013. Two paths are covered in `BatchAssociateVPPApps`: - **Existing team.** The dry-run early return moves below the existing `vpp.GetAssets` availability check. The check is read-only, so nothing is written on a dry run; it just runs before returning. - **Team that does not exist yet.** The dry run used to return immediately when `TeamByName` came back not found, because the team's token cannot be resolved before the team is created. It now validates the requested Apple apps against the assets of every unexpired VPP token, and returns the same error when an app is available on none of them. When no token exists at all it keeps the previous behaviour and skips, since the real apply already reports the missing token. Play Store entries are not VPP assets and are ignored. This addresses the "not alerted to in a dry run" half of #54013. #54023 adds the same check as a visible step in the GitOps repository template, for servers that predate this change. The graceful-failure half (the real apply halting and leaving other fleets without their apps) is not changed here. # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [x] Timeouts are implemented and retries are limited to avoid infinite loops (no new loops or retries; the added asset lookup uses the existing `vpp.GetAssets` client) ## Testing - [x] Added/updated automated tests - `TestBatchAssociateVPPAppsDryRunReportsMissingAssets`: existing team, dry run, licensed app passes and nothing is written; unlicensed app returns the same 422 as a real apply. - `TestBatchAssociateVPPAppsDryRunNewTeamReportsMissingAssets`: team not found, dry run, licensed app passes, unlicensed app fails, Play Store entry ignored, real apply for a missing team still returns not-found. - `TestGitOpsTeamVPPApps`: two new table cases run `fleetctl gitops --dry-run` end to end against the mock server, one with an available app (passes) and one with `team_vpp_invalid_app.yml` (fails with `app not available on vpp account`). The existing wet-run cases are unchanged. - [x] QA'd all new/changed functionality manually - Reproduced the original gap against a Fleet Cloud tenant on 4.92.0: a PR adding a fleet with an App Store ID whose license had been bought for a different app passed the dry run, then the apply failed with `requested app not available on vpp account` after the VPP re-apply had removed the other fleets' App Store apps. With this change the same configuration fails at `--dry-run`. ## AI **AI:** Claude Code (claude-fable-5-1) ## Notes for reviewers - The dry run for an existing team now makes one extra outbound call to Apple's VPP asset service per team with Apple apps, the same call the real apply already makes. - For a not-yet-existing team the dry run calls `GetAssets` once per unexpired token. Orgs with several tokens pay a few extra calls per new team on dry runs only. - Existing tests that run `--dry-run` with `app_store_apps` for new teams against a mock store whose `ListVPPTokens` returns nothing keep their previous behaviour because of the no-token skip. Tests that do return tokens will now validate the requested IDs against the fake VPP server's assets (`1` and `2` in `StartAndServeVPPServer`). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Dry runs now check whether Apple apps are available through unexpired VPP tokens, including when the team does not yet exist. * Dry runs report unavailable apps and expired-token errors without creating or assigning apps. * Dry runs also validate app versions, categories, platforms, and update windows before reporting whether the requested changes can be applied. * New-team dry runs require global VPP-app write access to check token availability; Android apps are validated without Apple asset checks. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: delize <4028612+delize@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This branch has not been deployed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Related issue: #54013
Adds a pre-apply check to the GitOps repository template that
fleetctl generate-gitopswrites. Beforefleetctl gitopsruns,.github/fleet-gitops/check-app-store-apps.shasks the server which App Store apps the VPP account can supply (GET /software/app_store_appsfor each fleet, plus the App Store apps already added viaGET /software/titles?available_for_install=true) and compares that with every numericapp_store_iddeclared underfleets/(orteams/). If any declared app is missing it fails the workflow with a::errorannotation naming the app, linking its App Store page and listing the files that declare it, so a wrong or unlicensed App Store ID is caught on the pull request instead of during the real apply.Companion to #54019, which makes
fleetctl gitops --dry-runitself return the availability error. This template step was requested in addition to that, so the check is visible in the generated workflow and works against servers that predate the server-side change.What changed:
templates/new/.github/fleet-gitops/check-app-store-apps.sh: bash, curl and jq only, matching the existinggitops.sh. Uses the sameFLEET_URLandFLEET_API_TOKENas the other steps. Handles both thefleets/fleet_idand the olderteams/team_idAPI vocabularies, pages through software titles, skips fleets without a VPP token, and ignores Play Store entries (bundle identifiers, not numeric IDs). The readable name comes from Apple's public lookup endpoint as a best-effort courtesy; the verdict never depends on it.action.template.yml: new step "Verify App Store apps are available on the VPP account" between "Configure fleetctl" and "Run fleetctl gitops commands".workflow.template.yml: the chmod step now covers both scripts, since the generator writes files as 0644..gitlab-ci.template.yml: runs the same script beforegitops.sh.new_test.go: the generated tree is expected to contain the new script.Checklist for submitter
changes/,orbit/changes/oree/fleetd-chrome/changes.See Changes files for more information.
SELECT *is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. (The script only interpolates numeric IDs extracted with[0-9]+and the server-provided fleet IDs, which are used as query parameters viacurl.)meta.has_next_resultsis false or a request fails; the catalog lookup has--max-time 8).Testing
TestNewBasicFileStructurenow expects the new script in the generated repository.fleetsand legacyteamsvocabularies, with all apps available (exit 0), with one unavailable app (exit 1,::error::Gable Visitor Management (6741767014, https://apps.apple.com/app/id6741767014) is not available on the VPP account. Declared in: ./fleets/other.yml ...), and with a bad token (curl fails, non-zero exit). The mock and the transcript are in the PR discussion on request.AI
AI: Claude Code (claude-fable-5-1)
Notes for reviewers
itunes.apple.comfrom the template is unwanted; it only supplies the display name in the error message.