镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Check App Store app availability in the GitOps template before applying - #54023

Draft
delize wants to merge 1 commit into
fleetdm:mainfrom
delize:templates-check-app-store-apps
Draft

delize wants to merge 1 commit into
fleetdm:mainfrom
delize:templates-check-app-store-apps

Conversation

@delize

@delize delize commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

Related issue: #54013

Adds a pre-apply check to the GitOps repository template that fleetctl generate-gitops writes. Before fleetctl gitops runs, .github/fleet-gitops/check-app-store-apps.sh asks the server which App Store apps the VPP account can supply (GET /software/app_store_apps for each fleet, plus the App Store apps already added via GET /software/titles?available_for_install=true) and compares that with every numeric app_store_id declared under fleets/ (or teams/). If any declared app is missing it fails the workflow with a ::error annotation 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-run itself 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 existing gitops.sh. Uses the same FLEET_URL and FLEET_API_TOKEN as the other steps. Handles both the fleets/fleet_id and the older teams/team_id API 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 before gitops.sh.
  • new_test.go: the generated tree is expected to contain the new script.

Checklist for submitter

  • Changes file added for user-visible changes in changes/, orbit/changes/ or ee/fleetd-chrome/changes.
    See Changes files for more information.
  • 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. (The script only interpolates numeric IDs extracted with [0-9]+ and the server-provided fleet IDs, which are used as query parameters via curl.)
  • Timeouts are implemented and retries are limited to avoid infinite loops (the titles page loop ends when meta.has_next_results is false or a request fails; the catalog lookup has --max-time 8).

Testing

  • Added/updated automated tests: TestNewBasicFileStructure now expects the new script in the generated repository.
  • QA'd all new/changed functionality manually: exercised the script against a small mock of the Fleet API in both the fleets and legacy teams vocabularies, 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

  • The generated workflow runs this on every event, including pushes and the nightly schedule, so a license that disappears later also stops the apply before catalogs are touched.
  • Fleets whose token call fails are skipped rather than treated as errors, so a repository with a mix of VPP and non-VPP fleets is not blocked by the non-VPP ones.
  • Happy to drop the Apple catalog lookup if a network call to itunes.apple.com from the template is unwanted; it only supplies the display name in the error message.

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>
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

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant