|
| 1 | +name: auto-update |
| 2 | + |
| 3 | +# Daily check against Microsoft's stable channel for a new VSCode |
| 4 | +# release. If the SHA on the latest-redirect doesn't match what the |
| 5 | +# recipe pins, open a PR that bumps the recipe (per-arch SRCREVs, |
| 6 | +# tarball timestamps, sha256 checksums, and the LICENSES.chromium.html |
| 7 | +# md5) and renames the file to vscode_<new-version>.bb. |
| 8 | +# |
| 9 | +# The PR is left for human review before merging -- automated bumps |
| 10 | +# of opaque binary blobs are exactly the case where a human eye on |
| 11 | +# the diff is valuable. |
| 12 | + |
| 13 | +on: |
| 14 | + schedule: |
| 15 | + # 10:42 UTC daily. Microsoft typically publishes stable releases |
| 16 | + # mid-month around 17:00-22:00 UTC; a morning-UTC poll picks them |
| 17 | + # up the next day reliably without piling on right after release. |
| 18 | + - cron: '42 10 * * *' |
| 19 | + workflow_dispatch: |
| 20 | + |
| 21 | +permissions: |
| 22 | + contents: write |
| 23 | + pull-requests: write |
| 24 | + |
| 25 | +jobs: |
| 26 | + check-and-update: |
| 27 | + runs-on: ubuntu-22.04 |
| 28 | + steps: |
| 29 | + - uses: actions/checkout@v4 |
| 30 | + with: |
| 31 | + # Need full history so we can detect whether an auto-update |
| 32 | + # branch with the new version already exists. |
| 33 | + fetch-depth: 0 |
| 34 | + |
| 35 | + - name: Resolve Microsoft's latest stable URLs |
| 36 | + id: resolve |
| 37 | + run: | |
| 38 | + set -eo pipefail |
| 39 | + for arch in x64 arm64 armhf; do |
| 40 | + url=$(curl -sLI "https://update.code.visualstudio.com/latest/linux-$arch/stable" \ |
| 41 | + | awk 'BEGIN{IGNORECASE=1} /^location:/{print $2}' \ |
| 42 | + | tr -d '\r' | tail -1) |
| 43 | + if [ -z "$url" ]; then |
| 44 | + echo "::error::failed to resolve URL for $arch" |
| 45 | + exit 1 |
| 46 | + fi |
| 47 | + # URL pattern: .../stable/<sha>/code-stable-<arch>-<timestamp>.tar.gz |
| 48 | + sha=$(echo "$url" | awk -F/ '{print $(NF-1)}') |
| 49 | + ts=$(echo "$url" | awk -F/ '{print $NF}' | sed -E "s/code-stable-${arch}-([0-9]+)\.tar\.gz/\1/") |
| 50 | + echo "$arch url: $url" |
| 51 | + echo "$arch sha: $sha" |
| 52 | + echo "$arch ts: $ts" |
| 53 | + echo "url_$arch=$url" >> "$GITHUB_OUTPUT" |
| 54 | + echo "sha_$arch=$sha" >> "$GITHUB_OUTPUT" |
| 55 | + echo "ts_$arch=$ts" >> "$GITHUB_OUTPUT" |
| 56 | + done |
| 57 | +
|
| 58 | + - name: Read current recipe state |
| 59 | + id: current |
| 60 | + run: | |
| 61 | + set -eo pipefail |
| 62 | + recipe=$(ls recipes-devtools/vscode/vscode_*.bb) |
| 63 | + ver=$(basename "$recipe" .bb | sed 's/^vscode_//') |
| 64 | + cur_sha=$(awk -F'"' '/^GIT_SHA[[:space:]]*=/ { print $2; exit }' "$recipe") |
| 65 | + echo "recipe=$recipe" >> "$GITHUB_OUTPUT" |
| 66 | + echo "version=$ver" >> "$GITHUB_OUTPUT" |
| 67 | + echo "git_sha=$cur_sha" >> "$GITHUB_OUTPUT" |
| 68 | + echo "Current: $recipe / version=$ver / GIT_SHA=$cur_sha" |
| 69 | +
|
| 70 | + - name: Skip if upstream SHA matches current recipe |
| 71 | + id: gate |
| 72 | + run: | |
| 73 | + set -eo pipefail |
| 74 | + # All three arches publish in lockstep under the same SHA; |
| 75 | + # if any of them diverge the recipe layout doesn't support |
| 76 | + # it anyway, so use x64 as the canonical signal. |
| 77 | + if [ "${{ steps.resolve.outputs.sha_x64 }}" = "${{ steps.current.outputs.git_sha }}" ]; then |
| 78 | + echo "Already at latest stable SHA ${{ steps.current.outputs.git_sha }}; nothing to do." |
| 79 | + echo "needs_update=false" >> "$GITHUB_OUTPUT" |
| 80 | + exit 0 |
| 81 | + fi |
| 82 | + echo "Upstream has new SHA: ${{ steps.resolve.outputs.sha_x64 }} (recipe pins ${{ steps.current.outputs.git_sha }})" |
| 83 | + echo "needs_update=true" >> "$GITHUB_OUTPUT" |
| 84 | +
|
| 85 | + - name: Fetch tarballs and compute checksums + license md5 |
| 86 | + if: steps.gate.outputs.needs_update == 'true' |
| 87 | + id: checksums |
| 88 | + run: | |
| 89 | + set -eo pipefail |
| 90 | + mkdir -p /tmp/vscode-tarballs |
| 91 | + for arch in x64 arm64 armhf; do |
| 92 | + url_var="url_$arch" |
| 93 | + url="${{ steps.resolve.outputs.url_x64 }}" |
| 94 | + # Re-resolve per-arch URL via output (matrix-style) |
| 95 | + case $arch in |
| 96 | + x64) url="${{ steps.resolve.outputs.url_x64 }}" ;; |
| 97 | + arm64) url="${{ steps.resolve.outputs.url_arm64 }}" ;; |
| 98 | + armhf) url="${{ steps.resolve.outputs.url_armhf }}" ;; |
| 99 | + esac |
| 100 | + echo "Downloading $arch from $url" |
| 101 | + curl -sLo "/tmp/vscode-tarballs/$arch.tgz" "$url" |
| 102 | + sum=$(sha256sum "/tmp/vscode-tarballs/$arch.tgz" | cut -d' ' -f1) |
| 103 | + echo "sha256_$arch=$sum" >> "$GITHUB_OUTPUT" |
| 104 | + echo "$arch: $sum" |
| 105 | + done |
| 106 | +
|
| 107 | + # All three tarballs ship the same LICENSES.chromium.html; |
| 108 | + # extract from x64 (cheapest) and md5 it. |
| 109 | + mkdir -p /tmp/vscode-extract |
| 110 | + tar xzf /tmp/vscode-tarballs/x64.tgz -C /tmp/vscode-extract \ |
| 111 | + VSCode-linux-x64/LICENSES.chromium.html |
| 112 | + lic_md5=$(md5sum /tmp/vscode-extract/VSCode-linux-x64/LICENSES.chromium.html \ |
| 113 | + | cut -d' ' -f1) |
| 114 | + echo "license_md5=$lic_md5" >> "$GITHUB_OUTPUT" |
| 115 | + echo "LICENSES.chromium.html md5: $lic_md5" |
| 116 | +
|
| 117 | + - name: Discover new version string |
| 118 | + if: steps.gate.outputs.needs_update == 'true' |
| 119 | + id: version |
| 120 | + run: | |
| 121 | + set -eo pipefail |
| 122 | + # Microsoft's `/api/update/.../stable/VERSION/.../release-notes` is one |
| 123 | + # source of the human-readable version. Simpler: parse the binary's |
| 124 | + # ProductDevelopment from package.json inside the tarball. |
| 125 | + mkdir -p /tmp/vscode-extract-meta |
| 126 | + tar xzf /tmp/vscode-tarballs/x64.tgz -C /tmp/vscode-extract-meta \ |
| 127 | + VSCode-linux-x64/resources/app/package.json |
| 128 | + ver=$(python3 -c "import json; print(json.load(open('/tmp/vscode-extract-meta/VSCode-linux-x64/resources/app/package.json'))['version'])") |
| 129 | + echo "Detected version: $ver" |
| 130 | + echo "version=$ver" >> "$GITHUB_OUTPUT" |
| 131 | +
|
| 132 | + - name: Bail if a branch for this version already exists |
| 133 | + if: steps.gate.outputs.needs_update == 'true' |
| 134 | + id: branch_check |
| 135 | + run: | |
| 136 | + set -eo pipefail |
| 137 | + branch="auto-update/vscode-${{ steps.version.outputs.version }}" |
| 138 | + if git ls-remote --heads origin "$branch" | grep -q "$branch"; then |
| 139 | + echo "::warning::branch $branch already exists upstream; skipping (assume PR already pending)" |
| 140 | + echo "skip=true" >> "$GITHUB_OUTPUT" |
| 141 | + else |
| 142 | + echo "skip=false" >> "$GITHUB_OUTPUT" |
| 143 | + echo "branch=$branch" >> "$GITHUB_OUTPUT" |
| 144 | + fi |
| 145 | +
|
| 146 | + - name: Update recipe in-place |
| 147 | + if: steps.gate.outputs.needs_update == 'true' && steps.branch_check.outputs.skip != 'true' |
| 148 | + run: | |
| 149 | + set -eo pipefail |
| 150 | + old_recipe="${{ steps.current.outputs.recipe }}" |
| 151 | + new_recipe="recipes-devtools/vscode/vscode_${{ steps.version.outputs.version }}.bb" |
| 152 | + git mv "$old_recipe" "$new_recipe" |
| 153 | +
|
| 154 | + # In-place value substitutions. Avoid sed -i's BSD/GNU split |
| 155 | + # by using Python. |
| 156 | + python3 - <<PYEOF |
| 157 | + import re, pathlib |
| 158 | + p = pathlib.Path("$new_recipe") |
| 159 | + s = p.read_text() |
| 160 | + subs = [ |
| 161 | + (r'^GIT_SHA = "[^"]*"', |
| 162 | + 'GIT_SHA = "${{ steps.resolve.outputs.sha_x64 }}"'), |
| 163 | + (r'^TIMESTAMP-x64 = "[^"]*"', |
| 164 | + 'TIMESTAMP-x64 = "${{ steps.resolve.outputs.ts_x64 }}"'), |
| 165 | + (r'^TIMESTAMP-arm64 = "[^"]*"', |
| 166 | + 'TIMESTAMP-arm64 = "${{ steps.resolve.outputs.ts_arm64 }}"'), |
| 167 | + (r'^TIMESTAMP-armhf = "[^"]*"', |
| 168 | + 'TIMESTAMP-armhf = "${{ steps.resolve.outputs.ts_armhf }}"'), |
| 169 | + (r'^SRC_URI\[vscode-x64\.sha256sum\][^=]*=\s*"[^"]*"', |
| 170 | + 'SRC_URI[vscode-x64.sha256sum] = "${{ steps.checksums.outputs.sha256_x64 }}"'), |
| 171 | + (r'^SRC_URI\[vscode-arm64\.sha256sum\][^=]*=\s*"[^"]*"', |
| 172 | + 'SRC_URI[vscode-arm64.sha256sum] = "${{ steps.checksums.outputs.sha256_arm64 }}"'), |
| 173 | + (r'^SRC_URI\[vscode-armhf\.sha256sum\][^=]*=\s*"[^"]*"', |
| 174 | + 'SRC_URI[vscode-armhf.sha256sum] = "${{ steps.checksums.outputs.sha256_armhf }}"'), |
| 175 | + (r'LICENSES\.chromium\.html;md5=[0-9a-f]+', |
| 176 | + 'LICENSES.chromium.html;md5=${{ steps.checksums.outputs.license_md5 }}'), |
| 177 | + ] |
| 178 | + for pat, repl in subs: |
| 179 | + s, n = re.subn(pat, repl, s, count=1, flags=re.MULTILINE) |
| 180 | + if n == 0: |
| 181 | + raise SystemExit(f"pattern not matched: {pat}") |
| 182 | + p.write_text(s) |
| 183 | + PYEOF |
| 184 | +
|
| 185 | + echo "--- diff ---" |
| 186 | + git diff -- "$new_recipe" || true |
| 187 | +
|
| 188 | + - name: Commit + push branch + open PR |
| 189 | + if: steps.gate.outputs.needs_update == 'true' && steps.branch_check.outputs.skip != 'true' |
| 190 | + env: |
| 191 | + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 192 | + run: | |
| 193 | + set -eo pipefail |
| 194 | + git config user.name "github-actions[bot]" |
| 195 | + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" |
| 196 | + branch="${{ steps.branch_check.outputs.branch }}" |
| 197 | + git checkout -b "$branch" |
| 198 | + git add -A |
| 199 | + git commit -m "vscode: bump to ${{ steps.version.outputs.version }} |
| 200 | +
|
| 201 | + Microsoft released VSCode ${{ steps.version.outputs.version }} (SHA |
| 202 | + ${{ steps.resolve.outputs.sha_x64 }}). Per-arch tarball timestamps: |
| 203 | + x64 ${{ steps.resolve.outputs.ts_x64 }} |
| 204 | + arm64 ${{ steps.resolve.outputs.ts_arm64 }} |
| 205 | + armhf ${{ steps.resolve.outputs.ts_armhf }} |
| 206 | + sha256 checksums and LICENSES.chromium.html md5 refreshed." |
| 207 | + git push -u origin "$branch" |
| 208 | + gh pr create \ |
| 209 | + --base main --head "$branch" \ |
| 210 | + --title "vscode: bump to ${{ steps.version.outputs.version }}" \ |
| 211 | + --body "Automated bump generated by .github/workflows/auto-update.yml. |
| 212 | +
|
| 213 | + Upstream details: |
| 214 | + - SHA: \`${{ steps.resolve.outputs.sha_x64 }}\` |
| 215 | + - Version: \`${{ steps.version.outputs.version }}\` |
| 216 | +
|
| 217 | + Per-arch tarballs: |
| 218 | + - x64: \`${{ steps.resolve.outputs.url_x64 }}\` |
| 219 | + - arm64: \`${{ steps.resolve.outputs.url_arm64 }}\` |
| 220 | + - armhf: \`${{ steps.resolve.outputs.url_armhf }}\` |
| 221 | +
|
| 222 | + sha256 checksums refreshed; LICENSES.chromium.html md5 re-extracted from the new x64 tarball. |
| 223 | +
|
| 224 | + CI will run the multi-arch parse + fetch matrix and the end-to-end scarthgap build. Eyeball the diff before merging since this is a proprietary binary bump." |
0 commit comments