/
/
/
1# Dependabot only understands requirements_all.txt, but that file is autogenerated from the
2# provider manifest.json files (see scripts/gen_requirements_all). A bump that lands only in
3# requirements_all.txt is reverted by the gen_requirements_all pre-commit hook, so CI fails.
4#
5# This workflow back-propagates the bumped pin into the owning manifest.json and regenerates
6# requirements_all.txt, then pushes the fix onto the Dependabot branch.
7
8name: Sync manifests on Dependabot PRs
9
10on:
11 pull_request_target:
12 types: [opened, synchronize, reopened]
13 branches:
14 - dev
15
16permissions:
17 contents: read
18
19env:
20 EXPECTED_APP_SLUG: musicassistant-bot
21 EXPECTED_APP_INSTALLATION_ID: "146062122"
22
23jobs:
24 sync-manifests:
25 name: Back-sync provider manifests
26 runs-on: ubuntu-latest
27 if: github.actor == 'dependabot[bot]'
28 permissions:
29 contents: read
30 steps:
31 # Keep the checkout token out of git config while processing the Dependabot branch.
32 # A repository-scoped musicassistant-bot[bot] token is minted only for the push so the
33 # resulting commit re-triggers the required checks.
34 - name: Check out the Dependabot branch
35 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
36 with:
37 ref: ${{ github.event.pull_request.head.ref }}
38 persist-credentials: false
39 fetch-depth: 0
40
41 - name: Determine packages bumped in requirements_all.txt
42 id: bumped
43 env:
44 BASE_REF: ${{ github.event.pull_request.base.ref }}
45 run: |
46 # Fetch the base branch so we can diff against the PR's true fork point and, later, run
47 # its current (trusted) tooling. base.ref (not base.sha) is used throughout: the base tip
48 # is the branch that actually carries the workflow + sync script, whereas base.sha may be
49 # an old fork point and is not reliably present in this shallow PR-head checkout.
50 git fetch --no-tags origin "$BASE_REF"
51 MERGE_BASE=$(git merge-base "origin/$BASE_REF" HEAD)
52 # Names of packages whose pin changed on the added side of the diff. The platform-
53 # conditional entries gen_requirements_all synthesises for alternate-index packages (the
54 # PyTorch CPU wheels, identified by their "platform_machine" marker) are excluded: those
55 # are never authored in a manifest, so the sync script skips them and including them here
56 # would only make gen_requirements_all revert the bump.
57 PACKAGES=$(git diff "$MERGE_BASE" HEAD -- requirements_all.txt \
58 | grep -E '^\+[^+-]' \
59 | grep -v 'platform_machine' \
60 | grep -oiE '^\+[A-Za-z0-9._-]+' \
61 | sed 's/^+//' | sort -u | tr '\n' ' ')
62 echo "packages=$PACKAGES" >> "$GITHUB_OUTPUT"
63 echo "Bumped packages: ${PACKAGES:-<none>}"
64
65 - name: Set up Python
66 if: steps.bumped.outputs.packages != ''
67 uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
68 with:
69 python-version-file: ".python-version"
70 check-latest: true
71
72 - name: Sync manifests and regenerate requirements_all.txt
73 if: steps.bumped.outputs.packages != ''
74 env:
75 # Pass via env (not inline ${{ }}) to avoid shell injection; the names are already
76 # constrained to [A-Za-z0-9._-] above, and word-splitting them into args is intended.
77 PACKAGES: ${{ steps.bumped.outputs.packages }}
78 BASE_REF: ${{ github.event.pull_request.base.ref }}
79 run: |
80 # Run the trusted base-branch tooling (fetched above) against the PR's data files: the PR
81 # branch may predate the sync script, and we must never execute scripts from the PR head.
82 # Unstage the overlay so it is never part of the commit pushed back to the branch.
83 git checkout "origin/$BASE_REF" -- scripts/
84 git reset -q -- scripts/
85 read -ra packages <<< "$PACKAGES"
86 python -m scripts.sync_manifest_from_requirements "${packages[@]}"
87 python -m scripts.gen_requirements_all
88
89 - name: Create Dependabot branch token
90 if: steps.bumped.outputs.packages != ''
91 id: push_token
92 uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
93 with:
94 client-id: ${{ vars.MUSIC_ASSISTANT_BOT_CLIENT_ID }}
95 private-key: ${{ secrets.MUSIC_ASSISTANT_BOT_PRIVATE_KEY }}
96 owner: ${{ github.repository_owner }}
97 repositories: ${{ github.event.repository.name }}
98 permission-contents: write
99
100 - name: Verify GitHub App identity
101 if: steps.bumped.outputs.packages != ''
102 env:
103 APP_SLUG: ${{ steps.push_token.outputs.app-slug }}
104 INSTALLATION_ID: ${{ steps.push_token.outputs.installation-id }}
105 run: |
106 if [ "$APP_SLUG" != "$EXPECTED_APP_SLUG" ] || \
107 [ "$INSTALLATION_ID" != "$EXPECTED_APP_INSTALLATION_ID" ]; then
108 echo "Unexpected GitHub App installation: $APP_SLUG/$INSTALLATION_ID" >&2
109 exit 1
110 fi
111
112 - name: Commit and push the manifest sync
113 if: steps.bumped.outputs.packages != ''
114 env:
115 PUSH_TOKEN: ${{ steps.push_token.outputs.token }}
116 HEAD_REF: ${{ github.event.pull_request.head.ref }}
117 REPO: ${{ github.repository }}
118 run: |
119 # Stage only the data files (the base-branch scripts/ overlay is left unstaged) and key
120 # the "nothing to push" check off the staged set, so unrelated working-tree noise from
121 # the overlay never triggers a push or sneaks into the commit.
122 git add music_assistant/providers/*/manifest.json requirements_all.txt
123 if git diff --cached --quiet; then
124 echo "Manifests already in sync, nothing to push"
125 exit 0
126 fi
127 git config user.name "dependabot[bot]"
128 git config user.email "49699333+dependabot[bot]@users.noreply.github.com"
129 git commit -m "Sync provider manifests with dependency bump"
130 git push "https://x-access-token:${PUSH_TOKEN}@github.com/${REPO}.git" "HEAD:$HEAD_REF"
131