fix(release): repair CI backup and publish to Rickub in 1.2.4
594f8e2 parent: 51e43db modified
.github/workflows/deploy-bunny.yml +98 -44 | @@ -141,34 +141,38 @@ jobs: | ||
| 141 | 141 | mkdir -p var/deploy-backup/files |
| 142 | 142 | : > var/deploy-backup/missing |
| 143 | 143 | |
| 144 | - while IFS= read -r -d '' file; do | |
| 145 | - relative_path="${file#var/config-site/}" | |
| 146 | - case "$relative_path" in | |
| 147 | - *[!A-Za-z0-9._/-]* | /* | *../*) | |
| 148 | - echo "Refusing unsafe backup path: $relative_path" >&2 | |
| 149 | - exit 1 | |
| 150 | - ;; | |
| 151 | - esac | |
| 152 | - | |
| 153 | - target="var/deploy-backup/files/$relative_path" | |
| 154 | - mkdir -p "$(dirname "$target")" | |
| 155 | - status="$(curl --silent --show-error \ | |
| 156 | - --output "$target" \ | |
| 157 | - --write-out '%{http_code}' \ | |
| 158 | - --header "AccessKey: $BUNNY_STORAGE_PASSWORD" \ | |
| 159 | - "$endpoint/$relative_path")" | |
| 160 | - case "$status" in | |
| 161 | - 200) ;; | |
| 162 | - 404) | |
| 163 | - rm -f "$target" | |
| 164 | - printf '%s\0' "$relative_path" >> var/deploy-backup/missing | |
| 165 | - ;; | |
| 166 | - *) | |
| 167 | - echo "Could not back up $relative_path: HTTP $status" >&2 | |
| 168 | - exit 1 | |
| 169 | - ;; | |
| 170 | - esac | |
| 171 | - done < <(find var/config-site -type f -print0) | |
| 144 | + # Feed the loop through a plain pipe. Process substitution exposes | |
| 145 | + # the file list as a /dev/fd path, which is unavailable on some | |
| 146 | + # hardened runners and aborts the backup before any upload. | |
| 147 | + find var/config-site -type f -print0 \ | |
| 148 | + | while IFS= read -r -d '' file; do | |
| 149 | + relative_path="${file#var/config-site/}" | |
| 150 | + case "$relative_path" in | |
| 151 | + *[!A-Za-z0-9._/-]* | /* | *../*) | |
| 152 | + echo "Refusing unsafe backup path: $relative_path" >&2 | |
| 153 | + exit 1 | |
| 154 | + ;; | |
| 155 | + esac | |
| 156 | + | |
| 157 | + target="var/deploy-backup/files/$relative_path" | |
| 158 | + mkdir -p "$(dirname "$target")" | |
| 159 | + status="$(curl --silent --show-error \ | |
| 160 | + --output "$target" \ | |
| 161 | + --write-out '%{http_code}' \ | |
| 162 | + --header "AccessKey: $BUNNY_STORAGE_PASSWORD" \ | |
| 163 | + "$endpoint/$relative_path")" | |
| 164 | + case "$status" in | |
| 165 | + 200) ;; | |
| 166 | + 404) | |
| 167 | + rm -f "$target" | |
| 168 | + printf '%s\0' "$relative_path" >> var/deploy-backup/missing | |
| 169 | + ;; | |
| 170 | + *) | |
| 171 | + echo "Could not back up $relative_path: HTTP $status" >&2 | |
| 172 | + exit 1 | |
| 173 | + ;; | |
| 174 | + esac | |
| 175 | + done | |
| 172 | 176 | |
| 173 | 177 | - name: Retain rollback bundle |
| 174 | 178 | uses: actions/upload-artifact@v4 |
| @@ -306,7 +310,7 @@ jobs: | ||
| 306 | 310 | "https://api.bunny.net/pullzone/$BUNNY_PULL_ZONE_ID/purgeCache" |
| 307 | 311 | |
| 308 | 312 | publish: |
| 309 | - name: Publish GitHub release notes | |
| 313 | + name: Publish Rickub release notes | |
| 310 | 314 | needs: [test, package, deploy] |
| 311 | 315 | if: ${{ startsWith(github.ref_name, 'v') }} |
| 312 | 316 | runs-on: ubuntu-latest |
| @@ -329,18 +333,54 @@ jobs: | ||
| 329 | 333 | - name: Generate release notes |
| 330 | 334 | run: node scripts/generate-release-notes.mjs |
| 331 | 335 | |
| 332 | - - name: Create release with changelog notes | |
| 333 | - env: | |
| 334 | - GH_TOKEN: ${{ github.token }} | |
| 336 | + - name: Select Rickub release API | |
| 337 | + id: forge_api | |
| 335 | 338 | shell: bash |
| 336 | 339 | run: | |
| 337 | 340 | set -euo pipefail |
| 338 | - gh release create "$GITHUB_REF_NAME" \ | |
| 339 | - --repo "$GITHUB_REPOSITORY" \ | |
| 340 | - --title "$GITHUB_REF_NAME" \ | |
| 341 | - --notes-file release-notes.md \ | |
| 342 | - --verify-tag \ | |
| 343 | - release-assets/* | |
| 341 | + if [[ -z "${GITHUB_API_URL:-}" ]]; then | |
| 342 | + echo "GITHUB_API_URL is required for Rickub releases" >&2 | |
| 343 | + exit 1 | |
| 344 | + fi | |
| 345 | + node -e 'const url = new URL(process.env.GITHUB_API_URL); if (!["http:", "https:"].includes(url.protocol) || url.hostname === "github.com" || url.hostname.endsWith(".github.com")) throw new Error("GITHUB_API_URL must point to the Rickub release API")' | |
| 346 | + printf 'api_url=%s\n' "$GITHUB_API_URL" >> "$GITHUB_OUTPUT" | |
| 347 | + - name: Publish Rickub release with changelog notes and artifacts | |
| 348 | + uses: actions/github-script@v7 | |
| 349 | + env: | |
| 350 | + RELEASE_TAG: ${{ github.ref_name }} | |
| 351 | + RICKUB_API_URL: ${{ steps.forge_api.outputs.api_url }} | |
| 352 | + RICKUB_SERVER_URL: ${{ github.server_url }} | |
| 353 | + with: | |
| 354 | + base-url: ${{ steps.forge_api.outputs.api_url }} | |
| 355 | + script: | | |
| 356 | + const fs = require("node:fs"); | |
| 357 | + const tag = process.env.RELEASE_TAG; | |
| 358 | + const { owner, repo } = context.repo; | |
| 359 | + const body = fs.readFileSync("release-notes.md", "utf8"); | |
| 360 | + const { data: release } = await github.rest.repos.createRelease({ | |
| 361 | + owner, repo, tag_name: tag, name: tag, body, | |
| 362 | + draft: false, prerelease: false, | |
| 363 | + }); | |
| 364 | + const uploadUrl = new URL(release.upload_url.replace(/\{.*$/u, "")); | |
| 365 | + const apiOrigin = new URL(process.env.RICKUB_API_URL).origin; | |
| 366 | + const forgeOrigin = new URL(process.env.RICKUB_SERVER_URL).origin; | |
| 367 | + if (!["http:", "https:"].includes(uploadUrl.protocol) || | |
| 368 | + uploadUrl.username || uploadUrl.password || | |
| 369 | + uploadUrl.hostname === "github.com" || uploadUrl.hostname.endsWith(".github.com") || | |
| 370 | + ![apiOrigin, forgeOrigin].includes(uploadUrl.origin)) { | |
| 371 | + throw new Error("Release asset upload URL must use the configured Rickub host"); | |
| 372 | + } | |
| 373 | + for (const name of [`lapin-fute-${tag}.pbw`, "SHA256SUMS"]) { | |
| 374 | + const bytes = fs.readFileSync(`release-assets/${name}`); | |
| 375 | + uploadUrl.searchParams.set("name", name); | |
| 376 | + await github.request(`POST ${uploadUrl}`, { | |
| 377 | + data: bytes, | |
| 378 | + headers: { | |
| 379 | + "content-type": "application/octet-stream", | |
| 380 | + "content-length": bytes.length, | |
| 381 | + }, | |
| 382 | + }); | |
| 383 | + } | |
| 344 | 384 | |
| 345 | 385 | store: |
| 346 | 386 | name: Publish release to the RePebble store |
| @@ -371,15 +411,29 @@ jobs: | ||
| 371 | 411 | name: lapin-fute-${{ github.ref_name }} |
| 372 | 412 | path: release-assets |
| 373 | 413 | |
| 374 | - - name: Fetch published release notes | |
| 375 | - env: | |
| 376 | - GH_TOKEN: ${{ github.token }} | |
| 377 | - RELEASE_TAG: ${{ github.ref_name }} | |
| 414 | + - name: Select Rickub release API | |
| 415 | + id: forge_api | |
| 378 | 416 | shell: bash |
| 379 | 417 | run: | |
| 380 | 418 | set -euo pipefail |
| 381 | - gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --json body --jq .body \ | |
| 382 | - > "$RUNNER_TEMP/store-release-notes.md" | |
| 419 | + if [[ -z "${GITHUB_API_URL:-}" ]]; then | |
| 420 | + echo "GITHUB_API_URL is required for Rickub releases" >&2 | |
| 421 | + exit 1 | |
| 422 | + fi | |
| 423 | + node -e 'const url = new URL(process.env.GITHUB_API_URL); if (!["http:", "https:"].includes(url.protocol) || url.hostname === "github.com" || url.hostname.endsWith(".github.com")) throw new Error("GITHUB_API_URL must point to the Rickub release API")' | |
| 424 | + printf 'api_url=%s\n' "$GITHUB_API_URL" >> "$GITHUB_OUTPUT" | |
| 425 | + - name: Fetch published Rickub release notes | |
| 426 | + uses: actions/github-script@v7 | |
| 427 | + env: | |
| 428 | + RELEASE_TAG: ${{ github.ref_name }} | |
| 429 | + with: | |
| 430 | + base-url: ${{ steps.forge_api.outputs.api_url }} | |
| 431 | + script: | | |
| 432 | + const fs = require("node:fs"); | |
| 433 | + const { data: release } = await github.rest.repos.getReleaseByTag({ | |
| 434 | + ...context.repo, tag: process.env.RELEASE_TAG, | |
| 435 | + }); | |
| 436 | + fs.writeFileSync(`${process.env.RUNNER_TEMP}/store-release-notes.md`, release.body); | |
| 383 | 437 | |
| 384 | 438 | - name: Publish to the RePebble store |
| 385 | 439 | env: |
| @@ -141,34 +141,38 @@ jobs: | |||
| 141 | mkdir -p var/deploy-backup/files | 141 | mkdir -p var/deploy-backup/files |
| 142 | : > var/deploy-backup/missing | 142 | : > var/deploy-backup/missing |
| 143 | 143 | ||
| 144 | - while IFS= read -r -d '' file; do | 144 | + # Feed the loop through a plain pipe. Process substitution exposes |
| 145 | - relative_path="${file#var/config-site/}" | 145 | + # the file list as a /dev/fd path, which is unavailable on some |
| 146 | - case "$relative_path" in | 146 | + # hardened runners and aborts the backup before any upload. |
| 147 | - *[!A-Za-z0-9._/-]* | /* | *../*) | 147 | + find var/config-site -type f -print0 \ |
| 148 | - echo "Refusing unsafe backup path: $relative_path" >&2 | 148 | + | while IFS= read -r -d '' file; do |
| 149 | - exit 1 | 149 | + relative_path="${file#var/config-site/}" |
| 150 | - ;; | 150 | + case "$relative_path" in |
| 151 | - esac | 151 | + *[!A-Za-z0-9._/-]* | /* | *../*) |
| 152 | - | 152 | + echo "Refusing unsafe backup path: $relative_path" >&2 |
| 153 | - target="var/deploy-backup/files/$relative_path" | 153 | + exit 1 |
| 154 | - mkdir -p "$(dirname "$target")" | 154 | + ;; |
| 155 | - status="$(curl --silent --show-error \ | 155 | + esac |
| 156 | - --output "$target" \ | 156 | + |
| 157 | - --write-out '%{http_code}' \ | 157 | + target="var/deploy-backup/files/$relative_path" |
| 158 | - --header "AccessKey: $BUNNY_STORAGE_PASSWORD" \ | 158 | + mkdir -p "$(dirname "$target")" |
| 159 | - "$endpoint/$relative_path")" | 159 | + status="$(curl --silent --show-error \ |
| 160 | - case "$status" in | 160 | + --output "$target" \ |
| 161 | - 200) ;; | 161 | + --write-out '%{http_code}' \ |
| 162 | - 404) | 162 | + --header "AccessKey: $BUNNY_STORAGE_PASSWORD" \ |
| 163 | - rm -f "$target" | 163 | + "$endpoint/$relative_path")" |
| 164 | - printf '%s\0' "$relative_path" >> var/deploy-backup/missing | 164 | + case "$status" in |
| 165 | - ;; | 165 | + 200) ;; |
| 166 | - *) | 166 | + 404) |
| 167 | - echo "Could not back up $relative_path: HTTP $status" >&2 | 167 | + rm -f "$target" |
| 168 | - exit 1 | 168 | + printf '%s\0' "$relative_path" >> var/deploy-backup/missing |
| 169 | - ;; | 169 | + ;; |
| 170 | - esac | 170 | + *) |
| 171 | - done < <(find var/config-site -type f -print0) | 171 | + echo "Could not back up $relative_path: HTTP $status" >&2 |
| 172 | + exit 1 | ||
| 173 | + ;; | ||
| 174 | + esac | ||
| 175 | + done | ||
| 172 | 176 | ||
| 173 | - name: Retain rollback bundle | 177 | - name: Retain rollback bundle |
| 174 | uses: actions/upload-artifact@v4 | 178 | uses: actions/upload-artifact@v4 |
| @@ -306,7 +310,7 @@ jobs: | |||
| 306 | "https://api.bunny.net/pullzone/$BUNNY_PULL_ZONE_ID/purgeCache" | 310 | "https://api.bunny.net/pullzone/$BUNNY_PULL_ZONE_ID/purgeCache" |
| 307 | 311 | ||
| 308 | publish: | 312 | publish: |
| 309 | - name: Publish GitHub release notes | 313 | + name: Publish Rickub release notes |
| 310 | needs: [test, package, deploy] | 314 | needs: [test, package, deploy] |
| 311 | if: ${{ startsWith(github.ref_name, 'v') }} | 315 | if: ${{ startsWith(github.ref_name, 'v') }} |
| 312 | runs-on: ubuntu-latest | 316 | runs-on: ubuntu-latest |
| @@ -329,18 +333,54 @@ jobs: | |||
| 329 | - name: Generate release notes | 333 | - name: Generate release notes |
| 330 | run: node scripts/generate-release-notes.mjs | 334 | run: node scripts/generate-release-notes.mjs |
| 331 | 335 | ||
| 332 | - - name: Create release with changelog notes | 336 | + - name: Select Rickub release API |
| 333 | - env: | 337 | + id: forge_api |
| 334 | - GH_TOKEN: ${{ github.token }} | ||
| 335 | shell: bash | 338 | shell: bash |
| 336 | run: | | 339 | run: | |
| 337 | set -euo pipefail | 340 | set -euo pipefail |
| 338 | - gh release create "$GITHUB_REF_NAME" \ | 341 | + if [[ -z "${GITHUB_API_URL:-}" ]]; then |
| 339 | - --repo "$GITHUB_REPOSITORY" \ | 342 | + echo "GITHUB_API_URL is required for Rickub releases" >&2 |
| 340 | - --title "$GITHUB_REF_NAME" \ | 343 | + exit 1 |
| 341 | - --notes-file release-notes.md \ | 344 | + fi |
| 342 | - --verify-tag \ | 345 | + node -e 'const url = new URL(process.env.GITHUB_API_URL); if (!["http:", "https:"].includes(url.protocol) || url.hostname === "github.com" || url.hostname.endsWith(".github.com")) throw new Error("GITHUB_API_URL must point to the Rickub release API")' |
| 343 | - release-assets/* | 346 | + printf 'api_url=%s\n' "$GITHUB_API_URL" >> "$GITHUB_OUTPUT" |
| 347 | + - name: Publish Rickub release with changelog notes and artifacts | ||
| 348 | + uses: actions/github-script@v7 | ||
| 349 | + env: | ||
| 350 | + RELEASE_TAG: ${{ github.ref_name }} | ||
| 351 | + RICKUB_API_URL: ${{ steps.forge_api.outputs.api_url }} | ||
| 352 | + RICKUB_SERVER_URL: ${{ github.server_url }} | ||
| 353 | + with: | ||
| 354 | + base-url: ${{ steps.forge_api.outputs.api_url }} | ||
| 355 | + script: | | ||
| 356 | + const fs = require("node:fs"); | ||
| 357 | + const tag = process.env.RELEASE_TAG; | ||
| 358 | + const { owner, repo } = context.repo; | ||
| 359 | + const body = fs.readFileSync("release-notes.md", "utf8"); | ||
| 360 | + const { data: release } = await github.rest.repos.createRelease({ | ||
| 361 | + owner, repo, tag_name: tag, name: tag, body, | ||
| 362 | + draft: false, prerelease: false, | ||
| 363 | + }); | ||
| 364 | + const uploadUrl = new URL(release.upload_url.replace(/\{.*$/u, "")); | ||
| 365 | + const apiOrigin = new URL(process.env.RICKUB_API_URL).origin; | ||
| 366 | + const forgeOrigin = new URL(process.env.RICKUB_SERVER_URL).origin; | ||
| 367 | + if (!["http:", "https:"].includes(uploadUrl.protocol) || | ||
| 368 | + uploadUrl.username || uploadUrl.password || | ||
| 369 | + uploadUrl.hostname === "github.com" || uploadUrl.hostname.endsWith(".github.com") || | ||
| 370 | + ![apiOrigin, forgeOrigin].includes(uploadUrl.origin)) { | ||
| 371 | + throw new Error("Release asset upload URL must use the configured Rickub host"); | ||
| 372 | + } | ||
| 373 | + for (const name of [`lapin-fute-${tag}.pbw`, "SHA256SUMS"]) { | ||
| 374 | + const bytes = fs.readFileSync(`release-assets/${name}`); | ||
| 375 | + uploadUrl.searchParams.set("name", name); | ||
| 376 | + await github.request(`POST ${uploadUrl}`, { | ||
| 377 | + data: bytes, | ||
| 378 | + headers: { | ||
| 379 | + "content-type": "application/octet-stream", | ||
| 380 | + "content-length": bytes.length, | ||
| 381 | + }, | ||
| 382 | + }); | ||
| 383 | + } | ||
| 344 | 384 | ||
| 345 | store: | 385 | store: |
| 346 | name: Publish release to the RePebble store | 386 | name: Publish release to the RePebble store |
| @@ -371,15 +411,29 @@ jobs: | |||
| 371 | name: lapin-fute-${{ github.ref_name }} | 411 | name: lapin-fute-${{ github.ref_name }} |
| 372 | path: release-assets | 412 | path: release-assets |
| 373 | 413 | ||
| 374 | - - name: Fetch published release notes | 414 | + - name: Select Rickub release API |
| 375 | - env: | 415 | + id: forge_api |
| 376 | - GH_TOKEN: ${{ github.token }} | ||
| 377 | - RELEASE_TAG: ${{ github.ref_name }} | ||
| 378 | shell: bash | 416 | shell: bash |
| 379 | run: | | 417 | run: | |
| 380 | set -euo pipefail | 418 | set -euo pipefail |
| 381 | - gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --json body --jq .body \ | 419 | + if [[ -z "${GITHUB_API_URL:-}" ]]; then |
| 382 | - > "$RUNNER_TEMP/store-release-notes.md" | 420 | + echo "GITHUB_API_URL is required for Rickub releases" >&2 |
| 421 | + exit 1 | ||
| 422 | + fi | ||
| 423 | + node -e 'const url = new URL(process.env.GITHUB_API_URL); if (!["http:", "https:"].includes(url.protocol) || url.hostname === "github.com" || url.hostname.endsWith(".github.com")) throw new Error("GITHUB_API_URL must point to the Rickub release API")' | ||
| 424 | + printf 'api_url=%s\n' "$GITHUB_API_URL" >> "$GITHUB_OUTPUT" | ||
| 425 | + - name: Fetch published Rickub release notes | ||
| 426 | + uses: actions/github-script@v7 | ||
| 427 | + env: | ||
| 428 | + RELEASE_TAG: ${{ github.ref_name }} | ||
| 429 | + with: | ||
| 430 | + base-url: ${{ steps.forge_api.outputs.api_url }} | ||
| 431 | + script: | | ||
| 432 | + const fs = require("node:fs"); | ||
| 433 | + const { data: release } = await github.rest.repos.getReleaseByTag({ | ||
| 434 | + ...context.repo, tag: process.env.RELEASE_TAG, | ||
| 435 | + }); | ||
| 436 | + fs.writeFileSync(`${process.env.RUNNER_TEMP}/store-release-notes.md`, release.body); | ||
| 383 | 437 | ||
| 384 | - name: Publish to the RePebble store | 438 | - name: Publish to the RePebble store |
| 385 | env: | 439 | env: |
modified
CHANGELOG.md +57 -42 | @@ -1,12 +1,27 @@ | ||
| 1 | 1 | # Changelog |
| 2 | 2 | |
| 3 | +## [1.2.4] - 2026-10-07 | |
| 4 | + | |
| 5 | +Release deployment backups now feed their file loop through a plain pipe instead of process substitution, so the backup can no longer fail on runners where `/dev/fd` is unavailable. | |
| 6 | + | |
| 7 | +Release publishing now uses the runner's Rickub API rather than relying on a GitHub CLI. The Store job retrieves the notes from that Rickub release. | |
| 8 | + | |
| 9 | +### Fixes | |
| 10 | + | |
| 11 | +- fix(deploy): avoid process substitution in production backup | |
| 12 | +- fix(release): publish through the configured forge API | |
| 13 | + | |
| 14 | +### Changes | |
| 15 | + | |
| 16 | +- chore(release): bump to version 1.2.4 | |
| 17 | + | |
| 3 | 18 | ## [1.2.3] - 2026-10-07 |
| 4 | 19 | |
| 5 | 20 | Release deployments install the pinned `pebble-tool` with a uv-managed Python 3.11 so SDK installation does not depend on the runner's system Python providing `ensurepip`. The production backup gate also names missing Bunny storage settings instead of failing silently. |
| 6 | 21 | |
| 7 | 22 | ### Fixes |
| 8 | 23 | |
| 9 | -- fix(deploy): use managed Python and report missing Bunny credentials (`07fd9ff`) | |
| 24 | +- fix(deploy): use managed Python and report missing Bunny credentials | |
| 10 | 25 | |
| 11 | 26 | ### Changes |
| 12 | 27 | |
| @@ -22,12 +37,12 @@ Release deployments now install the pinned `pebble-tool` with a uv-managed Pytho | ||
| 22 | 37 | |
| 23 | 38 | ### Fixes |
| 24 | 39 | |
| 25 | -- fix(traffic): accept up to 256 application periods per disruption (`1380da0`) | |
| 26 | -- fix(deploy): use managed Python and report missing Bunny credentials (`07fd9ff`) | |
| 40 | +- fix(traffic): accept up to 256 application periods per disruption | |
| 41 | +- fix(deploy): use managed Python and report missing Bunny credentials | |
| 27 | 42 | |
| 28 | 43 | ### Changes |
| 29 | 44 | |
| 30 | -- chore(release): bump to version 1.2.2 (`5736f05`) | |
| 45 | +- chore(release): bump to version 1.2.2 | |
| 31 | 46 | |
| 32 | 47 | ## [1.2.1] - 2026-09-20 |
| 33 | 48 | |
| @@ -37,12 +52,12 @@ Active works now participate in traffic reporting according to their severity, r | ||
| 37 | 52 | |
| 38 | 53 | ### Fixes |
| 39 | 54 | |
| 40 | -- fix(traffic): include active works in disruption selection (`a259698`) | |
| 55 | +- fix(traffic): include active works in disruption selection | |
| 41 | 56 | |
| 42 | 57 | ### Changes |
| 43 | 58 | |
| 44 | -- chore(release): bump to version 1.2.1 (`66c22cf`) | |
| 45 | -- ci(catalog): separate catalog publishing from release deployment (`8f2b8fe`) | |
| 59 | +- chore(release): bump to version 1.2.1 | |
| 60 | +- ci(catalog): separate catalog publishing from release deployment | |
| 46 | 61 | |
| 47 | 62 | ## [1.2.0] - 2026-09-20 |
| 48 | 63 | |
| @@ -52,7 +67,7 @@ If saving a replacement credential fails, the previous language and favorites re | ||
| 52 | 67 | |
| 53 | 68 | ### Features |
| 54 | 69 | |
| 55 | -- feat(configuration): add persistent watch language selection (#30) (#31) (`1650944`) | |
| 70 | +- feat(configuration): add persistent watch language selection (#30) (#31) | |
| 56 | 71 | |
| 57 | 72 | ### Changes |
| 58 | 73 | |
| @@ -68,11 +83,11 @@ Departure-to-arrival favorites require rebuilding and publishing the catalog wit | ||
| 68 | 83 | |
| 69 | 84 | ### Features |
| 70 | 85 | |
| 71 | -- feat(arrivals): support departure-to-arrival favorites (#28) (#29) (`102d719`) | |
| 86 | +- feat(arrivals): support departure-to-arrival favorites (#28) (#29) | |
| 72 | 87 | |
| 73 | 88 | ### Fixes |
| 74 | 89 | |
| 75 | -- fix(traffic): show the latest active incident and exclude works (`6ae56e9`) | |
| 90 | +- fix(traffic): show the latest active incident and exclude works | |
| 76 | 91 | |
| 77 | 92 | ### Changes |
| 78 | 93 | |
| @@ -84,12 +99,12 @@ This release fixes partial-service arrival pooling in the companion and relocate | ||
| 84 | 99 | |
| 85 | 100 | ### Changes |
| 86 | 101 | |
| 87 | -- chore(release): bump version to 1.0.4 (`bd3b82f`) | |
| 88 | -- docs: clarify stop and arrival terminology (`59caea0`) | |
| 102 | +- chore(release): bump version to 1.0.4 | |
| 103 | +- docs: clarify stop and arrival terminology | |
| 89 | 104 | |
| 90 | 105 | ### Fixes |
| 91 | 106 | |
| 92 | -- fix(companion): pool partial-service arrivals and move store assets (`2635f28`) | |
| 107 | +- fix(companion): pool partial-service arrivals and move store assets | |
| 93 | 108 | |
| 94 | 109 | **Full changelog**: https://github.com/Anthodev/lapin-fute/compare/v1.0.3...v1.0.4 |
| 95 | 110 | |
| @@ -101,7 +116,7 @@ This release clarifies stop and arrival terminology in the interface (#27). | ||
| 101 | 116 | |
| 102 | 117 | ### Fixes |
| 103 | 118 | |
| 104 | -- fix(ui): clarify stop and arrival terminology (#27) (`8835364`) | |
| 119 | +- fix(ui): clarify stop and arrival terminology (#27) | |
| 105 | 120 | |
| 106 | 121 | **Full changelog**: https://github.com/Anthodev/lapin-fute/compare/v1.0.2...v1.0.3 |
| 107 | 122 | |
| @@ -113,15 +128,15 @@ Line badges on the watch now carry a metro or tram prefix, so metro and tram lin | ||
| 113 | 128 | |
| 114 | 129 | ### Features |
| 115 | 130 | |
| 116 | -- feat(display): prefix metro and tram line labels on watch badges (#24) (#25) (`0cc3c0b`) | |
| 131 | +- feat(display): prefix metro and tram line labels on watch badges (#24) (#25) | |
| 117 | 132 | |
| 118 | 133 | ### Changes |
| 119 | 134 | |
| 120 | -- chore(docs): updated README.md (`1889044`) | |
| 135 | +- chore(docs): updated README.md | |
| 121 | 136 | |
| 122 | 137 | ### Fixes |
| 123 | 138 | |
| 124 | -- fix(release): handle squash merges when generating release notes (`9e36c16`) | |
| 139 | +- fix(release): handle squash merges when generating release notes | |
| 125 | 140 | |
| 126 | 141 | **Full changelog**: https://github.com/Anthodev/lapin-fute/compare/v1.0.1...v1.0.2 |
| 127 | 142 | |
| @@ -157,40 +172,40 @@ First public release. Lapin Futé puts Île-de-France arrivals on a Pebble watch | ||
| 157 | 172 | |
| 158 | 173 | ### Features |
| 159 | 174 | |
| 160 | -- feat: verify published configuration site (`9dc8049`) | |
| 161 | -- feat: harden stale and failure handling (#20) (`835b18b`) | |
| 162 | -- feat(companion): modernize settings and introduce app branding (#19) (`2799b0f`) | |
| 163 | -- feat: migrate to phone-only data and phone-prepared watch displays (#17) (`a46bf24`) | |
| 164 | -- feat: deliver Pebble watch consultation (#15) (`aabca5b`) | |
| 165 | -- feat: persist and synchronize favorites (#14) (`5ff79d1`) | |
| 166 | -- feat: build companion configuration page (#13) (`f7d01bd`) | |
| 167 | -- feat: normalize live PRIM departures (#12) (`5a0b437`) | |
| 168 | -- feat: establish Alloy app foundation (#10) (`ffd859a`) | |
| 175 | +- feat: verify published configuration site | |
| 176 | +- feat: harden stale and failure handling (#20) | |
| 177 | +- feat(companion): modernize settings and introduce app branding (#19) | |
| 178 | +- feat: migrate to phone-only data and phone-prepared watch displays (#17) | |
| 179 | +- feat: deliver Pebble watch consultation (#15) | |
| 180 | +- feat: persist and synchronize favorites (#14) | |
| 181 | +- feat: build companion configuration page (#13) | |
| 182 | +- feat: normalize live PRIM departures (#12) | |
| 183 | +- feat: establish Alloy app foundation (#10) | |
| 169 | 184 | |
| 170 | 185 | ### Changes |
| 171 | 186 | |
| 172 | -- docs: link published RePebble release (`de5dd7f`) | |
| 173 | -- chore: add RePebble store screenshots and banner (`e008516`) | |
| 174 | -- chore: add RePebble store icons (`646389f`) | |
| 175 | -- docs: streamline project overview and controls (`3e430d6`) | |
| 176 | -- docs: add v1 release checklist (`d6184a2`) | |
| 177 | -- docs: research official v1 release requirements (`30a8f4c`) | |
| 178 | -- chore: ignore Pebble build lock (`6b61693`) | |
| 179 | -- chore: set release version to 1.0.0 (`e1d539d`) | |
| 180 | -- Resolve IDFM service catalog with bounded importer (#11) (`acf5bba`) | |
| 181 | -- chore: initialize develop branch (`4c1dead`) | |
| 187 | +- docs: link published RePebble release | |
| 188 | +- chore: add RePebble store screenshots and banner | |
| 189 | +- chore: add RePebble store icons | |
| 190 | +- docs: streamline project overview and controls | |
| 191 | +- docs: add v1 release checklist | |
| 192 | +- docs: research official v1 release requirements | |
| 193 | +- chore: ignore Pebble build lock | |
| 194 | +- chore: set release version to 1.0.0 | |
| 195 | +- Resolve IDFM service catalog with bounded importer (#11) | |
| 196 | +- chore: initialize develop branch | |
| 182 | 197 | |
| 183 | 198 | ### Fixes |
| 184 | 199 | |
| 185 | -- fix: normalize Emery store screenshot brightness (`befc545`) | |
| 186 | -- fix: close release verification gaps (`457bcb9`) | |
| 200 | +- fix: normalize Emery store screenshot brightness | |
| 201 | +- fix: close release verification gaps | |
| 187 | 202 | |
| 188 | 203 | ### CI |
| 189 | 204 | |
| 190 | -- ci: skip unchanged catalog uploads (`91a4982`) | |
| 191 | -- ci: retain and rehearse deployment rollback (`5f1ff8c`) | |
| 192 | -- ci: package PBW release assets (`30f04fc`) | |
| 193 | -- ci: verify deploys and restore failures (`f224ada`) | |
| 205 | +- ci: skip unchanged catalog uploads | |
| 206 | +- ci: retain and rehearse deployment rollback | |
| 207 | +- ci: package PBW release assets | |
| 208 | +- ci: verify deploys and restore failures | |
| 194 | 209 | |
| 195 | 210 | **Full changelog**: https://github.com/Anthodev/lapin-fute/commits/v1.0.0 |
| 196 | 211 | |
| @@ -1,12 +1,27 @@ | |||
| 1 | # Changelog | 1 | # Changelog |
| 2 | 2 | ||
| 3 | +## [1.2.4] - 2026-10-07 | ||
| 4 | + | ||
| 5 | +Release deployment backups now feed their file loop through a plain pipe instead of process substitution, so the backup can no longer fail on runners where `/dev/fd` is unavailable. | ||
| 6 | + | ||
| 7 | +Release publishing now uses the runner's Rickub API rather than relying on a GitHub CLI. The Store job retrieves the notes from that Rickub release. | ||
| 8 | + | ||
| 9 | +### Fixes | ||
| 10 | + | ||
| 11 | +- fix(deploy): avoid process substitution in production backup | ||
| 12 | +- fix(release): publish through the configured forge API | ||
| 13 | + | ||
| 14 | +### Changes | ||
| 15 | + | ||
| 16 | +- chore(release): bump to version 1.2.4 | ||
| 17 | + | ||
| 3 | ## [1.2.3] - 2026-10-07 | 18 | ## [1.2.3] - 2026-10-07 |
| 4 | 19 | ||
| 5 | Release deployments install the pinned `pebble-tool` with a uv-managed Python 3.11 so SDK installation does not depend on the runner's system Python providing `ensurepip`. The production backup gate also names missing Bunny storage settings instead of failing silently. | 20 | Release deployments install the pinned `pebble-tool` with a uv-managed Python 3.11 so SDK installation does not depend on the runner's system Python providing `ensurepip`. The production backup gate also names missing Bunny storage settings instead of failing silently. |
| 6 | 21 | ||
| 7 | ### Fixes | 22 | ### Fixes |
| 8 | 23 | ||
| 9 | -- fix(deploy): use managed Python and report missing Bunny credentials (`07fd9ff`) | 24 | +- fix(deploy): use managed Python and report missing Bunny credentials |
| 10 | 25 | ||
| 11 | ### Changes | 26 | ### Changes |
| 12 | 27 | ||
| @@ -22,12 +37,12 @@ Release deployments now install the pinned `pebble-tool` with a uv-managed Pytho | |||
| 22 | 37 | ||
| 23 | ### Fixes | 38 | ### Fixes |
| 24 | 39 | ||
| 25 | -- fix(traffic): accept up to 256 application periods per disruption (`1380da0`) | 40 | +- fix(traffic): accept up to 256 application periods per disruption |
| 26 | -- fix(deploy): use managed Python and report missing Bunny credentials (`07fd9ff`) | 41 | +- fix(deploy): use managed Python and report missing Bunny credentials |
| 27 | 42 | ||
| 28 | ### Changes | 43 | ### Changes |
| 29 | 44 | ||
| 30 | -- chore(release): bump to version 1.2.2 (`5736f05`) | 45 | +- chore(release): bump to version 1.2.2 |
| 31 | 46 | ||
| 32 | ## [1.2.1] - 2026-09-20 | 47 | ## [1.2.1] - 2026-09-20 |
| 33 | 48 | ||
| @@ -37,12 +52,12 @@ Active works now participate in traffic reporting according to their severity, r | |||
| 37 | 52 | ||
| 38 | ### Fixes | 53 | ### Fixes |
| 39 | 54 | ||
| 40 | -- fix(traffic): include active works in disruption selection (`a259698`) | 55 | +- fix(traffic): include active works in disruption selection |
| 41 | 56 | ||
| 42 | ### Changes | 57 | ### Changes |
| 43 | 58 | ||
| 44 | -- chore(release): bump to version 1.2.1 (`66c22cf`) | 59 | +- chore(release): bump to version 1.2.1 |
| 45 | -- ci(catalog): separate catalog publishing from release deployment (`8f2b8fe`) | 60 | +- ci(catalog): separate catalog publishing from release deployment |
| 46 | 61 | ||
| 47 | ## [1.2.0] - 2026-09-20 | 62 | ## [1.2.0] - 2026-09-20 |
| 48 | 63 | ||
| @@ -52,7 +67,7 @@ If saving a replacement credential fails, the previous language and favorites re | |||
| 52 | 67 | ||
| 53 | ### Features | 68 | ### Features |
| 54 | 69 | ||
| 55 | -- feat(configuration): add persistent watch language selection (#30) (#31) (`1650944`) | 70 | +- feat(configuration): add persistent watch language selection (#30) (#31) |
| 56 | 71 | ||
| 57 | ### Changes | 72 | ### Changes |
| 58 | 73 | ||
| @@ -68,11 +83,11 @@ Departure-to-arrival favorites require rebuilding and publishing the catalog wit | |||
| 68 | 83 | ||
| 69 | ### Features | 84 | ### Features |
| 70 | 85 | ||
| 71 | -- feat(arrivals): support departure-to-arrival favorites (#28) (#29) (`102d719`) | 86 | +- feat(arrivals): support departure-to-arrival favorites (#28) (#29) |
| 72 | 87 | ||
| 73 | ### Fixes | 88 | ### Fixes |
| 74 | 89 | ||
| 75 | -- fix(traffic): show the latest active incident and exclude works (`6ae56e9`) | 90 | +- fix(traffic): show the latest active incident and exclude works |
| 76 | 91 | ||
| 77 | ### Changes | 92 | ### Changes |
| 78 | 93 | ||
| @@ -84,12 +99,12 @@ This release fixes partial-service arrival pooling in the companion and relocate | |||
| 84 | 99 | ||
| 85 | ### Changes | 100 | ### Changes |
| 86 | 101 | ||
| 87 | -- chore(release): bump version to 1.0.4 (`bd3b82f`) | 102 | +- chore(release): bump version to 1.0.4 |
| 88 | -- docs: clarify stop and arrival terminology (`59caea0`) | 103 | +- docs: clarify stop and arrival terminology |
| 89 | 104 | ||
| 90 | ### Fixes | 105 | ### Fixes |
| 91 | 106 | ||
| 92 | -- fix(companion): pool partial-service arrivals and move store assets (`2635f28`) | 107 | +- fix(companion): pool partial-service arrivals and move store assets |
| 93 | 108 | ||
| 94 | **Full changelog**: https://github.com/Anthodev/lapin-fute/compare/v1.0.3...v1.0.4 | 109 | **Full changelog**: https://github.com/Anthodev/lapin-fute/compare/v1.0.3...v1.0.4 |
| 95 | 110 | ||
| @@ -101,7 +116,7 @@ This release clarifies stop and arrival terminology in the interface (#27). | |||
| 101 | 116 | ||
| 102 | ### Fixes | 117 | ### Fixes |
| 103 | 118 | ||
| 104 | -- fix(ui): clarify stop and arrival terminology (#27) (`8835364`) | 119 | +- fix(ui): clarify stop and arrival terminology (#27) |
| 105 | 120 | ||
| 106 | **Full changelog**: https://github.com/Anthodev/lapin-fute/compare/v1.0.2...v1.0.3 | 121 | **Full changelog**: https://github.com/Anthodev/lapin-fute/compare/v1.0.2...v1.0.3 |
| 107 | 122 | ||
| @@ -113,15 +128,15 @@ Line badges on the watch now carry a metro or tram prefix, so metro and tram lin | |||
| 113 | 128 | ||
| 114 | ### Features | 129 | ### Features |
| 115 | 130 | ||
| 116 | -- feat(display): prefix metro and tram line labels on watch badges (#24) (#25) (`0cc3c0b`) | 131 | +- feat(display): prefix metro and tram line labels on watch badges (#24) (#25) |
| 117 | 132 | ||
| 118 | ### Changes | 133 | ### Changes |
| 119 | 134 | ||
| 120 | -- chore(docs): updated README.md (`1889044`) | 135 | +- chore(docs): updated README.md |
| 121 | 136 | ||
| 122 | ### Fixes | 137 | ### Fixes |
| 123 | 138 | ||
| 124 | -- fix(release): handle squash merges when generating release notes (`9e36c16`) | 139 | +- fix(release): handle squash merges when generating release notes |
| 125 | 140 | ||
| 126 | **Full changelog**: https://github.com/Anthodev/lapin-fute/compare/v1.0.1...v1.0.2 | 141 | **Full changelog**: https://github.com/Anthodev/lapin-fute/compare/v1.0.1...v1.0.2 |
| 127 | 142 | ||
| @@ -157,40 +172,40 @@ First public release. Lapin Futé puts Île-de-France arrivals on a Pebble watch | |||
| 157 | 172 | ||
| 158 | ### Features | 173 | ### Features |
| 159 | 174 | ||
| 160 | -- feat: verify published configuration site (`9dc8049`) | 175 | +- feat: verify published configuration site |
| 161 | -- feat: harden stale and failure handling (#20) (`835b18b`) | 176 | +- feat: harden stale and failure handling (#20) |
| 162 | -- feat(companion): modernize settings and introduce app branding (#19) (`2799b0f`) | 177 | +- feat(companion): modernize settings and introduce app branding (#19) |
| 163 | -- feat: migrate to phone-only data and phone-prepared watch displays (#17) (`a46bf24`) | 178 | +- feat: migrate to phone-only data and phone-prepared watch displays (#17) |
| 164 | -- feat: deliver Pebble watch consultation (#15) (`aabca5b`) | 179 | +- feat: deliver Pebble watch consultation (#15) |
| 165 | -- feat: persist and synchronize favorites (#14) (`5ff79d1`) | 180 | +- feat: persist and synchronize favorites (#14) |
| 166 | -- feat: build companion configuration page (#13) (`f7d01bd`) | 181 | +- feat: build companion configuration page (#13) |
| 167 | -- feat: normalize live PRIM departures (#12) (`5a0b437`) | 182 | +- feat: normalize live PRIM departures (#12) |
| 168 | -- feat: establish Alloy app foundation (#10) (`ffd859a`) | 183 | +- feat: establish Alloy app foundation (#10) |
| 169 | 184 | ||
| 170 | ### Changes | 185 | ### Changes |
| 171 | 186 | ||
| 172 | -- docs: link published RePebble release (`de5dd7f`) | 187 | +- docs: link published RePebble release |
| 173 | -- chore: add RePebble store screenshots and banner (`e008516`) | 188 | +- chore: add RePebble store screenshots and banner |
| 174 | -- chore: add RePebble store icons (`646389f`) | 189 | +- chore: add RePebble store icons |
| 175 | -- docs: streamline project overview and controls (`3e430d6`) | 190 | +- docs: streamline project overview and controls |
| 176 | -- docs: add v1 release checklist (`d6184a2`) | 191 | +- docs: add v1 release checklist |
| 177 | -- docs: research official v1 release requirements (`30a8f4c`) | 192 | +- docs: research official v1 release requirements |
| 178 | -- chore: ignore Pebble build lock (`6b61693`) | 193 | +- chore: ignore Pebble build lock |
| 179 | -- chore: set release version to 1.0.0 (`e1d539d`) | 194 | +- chore: set release version to 1.0.0 |
| 180 | -- Resolve IDFM service catalog with bounded importer (#11) (`acf5bba`) | 195 | +- Resolve IDFM service catalog with bounded importer (#11) |
| 181 | -- chore: initialize develop branch (`4c1dead`) | 196 | +- chore: initialize develop branch |
| 182 | 197 | ||
| 183 | ### Fixes | 198 | ### Fixes |
| 184 | 199 | ||
| 185 | -- fix: normalize Emery store screenshot brightness (`befc545`) | 200 | +- fix: normalize Emery store screenshot brightness |
| 186 | -- fix: close release verification gaps (`457bcb9`) | 201 | +- fix: close release verification gaps |
| 187 | 202 | ||
| 188 | ### CI | 203 | ### CI |
| 189 | 204 | ||
| 190 | -- ci: skip unchanged catalog uploads (`91a4982`) | 205 | +- ci: skip unchanged catalog uploads |
| 191 | -- ci: retain and rehearse deployment rollback (`5f1ff8c`) | 206 | +- ci: retain and rehearse deployment rollback |
| 192 | -- ci: package PBW release assets (`30f04fc`) | 207 | +- ci: package PBW release assets |
| 193 | -- ci: verify deploys and restore failures (`f224ada`) | 208 | +- ci: verify deploys and restore failures |
| 194 | 209 | ||
| 195 | **Full changelog**: https://github.com/Anthodev/lapin-fute/commits/v1.0.0 | 210 | **Full changelog**: https://github.com/Anthodev/lapin-fute/commits/v1.0.0 |
| 196 | 211 | ||
modified
README.md +2 -2 | @@ -110,9 +110,9 @@ npm run build:config-site # assemble var/config-site; f | ||
| 110 | 110 | |
| 111 | 111 | `node --env-file=.env scripts/probe-catalog.mjs` runs a small live check against PRIM, one request per transport mode, and needs a `PRIM_API_KEY`. Operator tokens live only in `.env` and are never printed or embedded in build output. |
| 112 | 112 | |
| 113 | -To rebuild the hosted catalog independently, run **Rebuild and publish IDFM catalog** from GitHub Actions, using `.github/workflows/rebuild-catalog.yml`. It uses the existing `production` environment's IDFM and Bunny credentials and publishes only catalog files, with up to 75 concurrent curl uploads. The manifest is uploaded last. It does not build the PBW, deploy the settings page, or create a GitHub release. | |
| 113 | +To rebuild the hosted catalog independently, run **Rebuild and publish IDFM catalog** from Rickub Actions, using `.github/workflows/rebuild-catalog.yml`. It uses the existing `production` environment's IDFM and Bunny credentials and publishes only catalog files, with up to 75 concurrent curl uploads. The manifest is uploaded last. It does not build the PBW, deploy the settings page, or create a Rickub release. | |
| 114 | 114 | |
| 115 | -Uploads are skipped when the hosted IDFM source revision is unchanged. Enable `force_catalog_upload` when republishing a catalog format change from the same source data. The workflow retains the previous manifest for rollback and shares the production publication lock with page deployments and rollbacks. Release tags continue through `deploy-bunny.yml`, without rebuilding the catalog. Release deployments install the pinned `pebble-tool` on a uv-managed Python 3.11, because the runner's system Python has no `ensurepip`, and require the `production` environment's `BUNNY_STORAGE_ENDPOINT` variable and `BUNNY_STORAGE_PASSWORD` secret; when either is unset, the deployment fails at the backup gate with an error naming the missing one. | |
| 115 | +Uploads are skipped when the hosted IDFM source revision is unchanged. Enable `force_catalog_upload` when republishing a catalog format change from the same source data. The workflow retains the previous manifest for rollback and shares the production publication lock with page deployments and rollbacks. Release tags continue through `deploy-bunny.yml`, without rebuilding the catalog. Release deployments install the pinned `pebble-tool` on a uv-managed Python 3.11, because the runner's system Python has no `ensurepip`, and require the `production` environment's `BUNNY_STORAGE_ENDPOINT` variable and `BUNNY_STORAGE_PASSWORD` secret; when either is unset, the deployment fails at the backup gate with an error naming the missing one. The backup step feeds its file loop through a plain pipe rather than process substitution, which hardened runners may not provide. After tests, packaging, and deployment pass, the publish job creates a Rickub release through the runner's `GITHUB_API_URL` with the tagged changelog notes and attaches the PBW and `SHA256SUMS`. The Store job reads those published notes through the same API. | |
| 116 | 116 | |
| 117 | 117 | ## Contributing |
| 118 | 118 | |
| @@ -110,9 +110,9 @@ npm run build:config-site # assemble var/config-site; f | |||
| 110 | 110 | ||
| 111 | `node --env-file=.env scripts/probe-catalog.mjs` runs a small live check against PRIM, one request per transport mode, and needs a `PRIM_API_KEY`. Operator tokens live only in `.env` and are never printed or embedded in build output. | 111 | `node --env-file=.env scripts/probe-catalog.mjs` runs a small live check against PRIM, one request per transport mode, and needs a `PRIM_API_KEY`. Operator tokens live only in `.env` and are never printed or embedded in build output. |
| 112 | 112 | ||
| 113 | -To rebuild the hosted catalog independently, run **Rebuild and publish IDFM catalog** from GitHub Actions, using `.github/workflows/rebuild-catalog.yml`. It uses the existing `production` environment's IDFM and Bunny credentials and publishes only catalog files, with up to 75 concurrent curl uploads. The manifest is uploaded last. It does not build the PBW, deploy the settings page, or create a GitHub release. | 113 | +To rebuild the hosted catalog independently, run **Rebuild and publish IDFM catalog** from Rickub Actions, using `.github/workflows/rebuild-catalog.yml`. It uses the existing `production` environment's IDFM and Bunny credentials and publishes only catalog files, with up to 75 concurrent curl uploads. The manifest is uploaded last. It does not build the PBW, deploy the settings page, or create a Rickub release. |
| 114 | 114 | ||
| 115 | -Uploads are skipped when the hosted IDFM source revision is unchanged. Enable `force_catalog_upload` when republishing a catalog format change from the same source data. The workflow retains the previous manifest for rollback and shares the production publication lock with page deployments and rollbacks. Release tags continue through `deploy-bunny.yml`, without rebuilding the catalog. Release deployments install the pinned `pebble-tool` on a uv-managed Python 3.11, because the runner's system Python has no `ensurepip`, and require the `production` environment's `BUNNY_STORAGE_ENDPOINT` variable and `BUNNY_STORAGE_PASSWORD` secret; when either is unset, the deployment fails at the backup gate with an error naming the missing one. | 115 | +Uploads are skipped when the hosted IDFM source revision is unchanged. Enable `force_catalog_upload` when republishing a catalog format change from the same source data. The workflow retains the previous manifest for rollback and shares the production publication lock with page deployments and rollbacks. Release tags continue through `deploy-bunny.yml`, without rebuilding the catalog. Release deployments install the pinned `pebble-tool` on a uv-managed Python 3.11, because the runner's system Python has no `ensurepip`, and require the `production` environment's `BUNNY_STORAGE_ENDPOINT` variable and `BUNNY_STORAGE_PASSWORD` secret; when either is unset, the deployment fails at the backup gate with an error naming the missing one. The backup step feeds its file loop through a plain pipe rather than process substitution, which hardened runners may not provide. After tests, packaging, and deployment pass, the publish job creates a Rickub release through the runner's `GITHUB_API_URL` with the tagged changelog notes and attaches the PBW and `SHA256SUMS`. The Store job reads those published notes through the same API. |
| 116 | 116 | ||
| 117 | ## Contributing | 117 | ## Contributing |
| 118 | 118 | ||
modified
package-lock.json +6 -6 | @@ -1,12 +1,12 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "name": "lapin-fute", |
| 3 | - "version": "1.2.3", | |
| 3 | + "version": "1.2.4", | |
| 4 | 4 | "lockfileVersion": 3, |
| 5 | 5 | "requires": true, |
| 6 | 6 | "packages": { |
| 7 | 7 | "": { |
| 8 | 8 | "name": "lapin-fute", |
| 9 | - "version": "1.2.3", | |
| 9 | + "version": "1.2.4", | |
| 10 | 10 | "license": "MIT", |
| 11 | 11 | "workspaces": [ |
| 12 | 12 | "packages/*" |
| @@ -57,7 +57,7 @@ | ||
| 57 | 57 | }, |
| 58 | 58 | "packages/catalog": { |
| 59 | 59 | "name": "@lapin-fute/catalog", |
| 60 | - "version": "1.2.3", | |
| 60 | + "version": "1.2.4", | |
| 61 | 61 | "license": "MIT", |
| 62 | 62 | "dependencies": { |
| 63 | 63 | "csv-parse": "7.0.2", |
| @@ -66,17 +66,17 @@ | ||
| 66 | 66 | }, |
| 67 | 67 | "packages/companion": { |
| 68 | 68 | "name": "@lapin-fute/companion", |
| 69 | - "version": "1.2.3", | |
| 69 | + "version": "1.2.4", | |
| 70 | 70 | "license": "MIT" |
| 71 | 71 | }, |
| 72 | 72 | "packages/config-page": { |
| 73 | 73 | "name": "@lapin-fute/config-page", |
| 74 | - "version": "1.2.3", | |
| 74 | + "version": "1.2.4", | |
| 75 | 75 | "license": "MIT" |
| 76 | 76 | }, |
| 77 | 77 | "packages/watch": { |
| 78 | 78 | "name": "lapin-fute-watch", |
| 79 | - "version": "1.2.3", | |
| 79 | + "version": "1.2.4", | |
| 80 | 80 | "license": "MIT" |
| 81 | 81 | } |
| 82 | 82 | } |
| @@ -1,12 +1,12 @@ | |||
| 1 | { | 1 | { |
| 2 | "name": "lapin-fute", | 2 | "name": "lapin-fute", |
| 3 | - "version": "1.2.3", | 3 | + "version": "1.2.4", |
| 4 | "lockfileVersion": 3, | 4 | "lockfileVersion": 3, |
| 5 | "requires": true, | 5 | "requires": true, |
| 6 | "packages": { | 6 | "packages": { |
| 7 | "": { | 7 | "": { |
| 8 | "name": "lapin-fute", | 8 | "name": "lapin-fute", |
| 9 | - "version": "1.2.3", | 9 | + "version": "1.2.4", |
| 10 | "license": "MIT", | 10 | "license": "MIT", |
| 11 | "workspaces": [ | 11 | "workspaces": [ |
| 12 | "packages/*" | 12 | "packages/*" |
| @@ -57,7 +57,7 @@ | |||
| 57 | }, | 57 | }, |
| 58 | "packages/catalog": { | 58 | "packages/catalog": { |
| 59 | "name": "@lapin-fute/catalog", | 59 | "name": "@lapin-fute/catalog", |
| 60 | - "version": "1.2.3", | 60 | + "version": "1.2.4", |
| 61 | "license": "MIT", | 61 | "license": "MIT", |
| 62 | "dependencies": { | 62 | "dependencies": { |
| 63 | "csv-parse": "7.0.2", | 63 | "csv-parse": "7.0.2", |
| @@ -66,17 +66,17 @@ | |||
| 66 | }, | 66 | }, |
| 67 | "packages/companion": { | 67 | "packages/companion": { |
| 68 | "name": "@lapin-fute/companion", | 68 | "name": "@lapin-fute/companion", |
| 69 | - "version": "1.2.3", | 69 | + "version": "1.2.4", |
| 70 | "license": "MIT" | 70 | "license": "MIT" |
| 71 | }, | 71 | }, |
| 72 | "packages/config-page": { | 72 | "packages/config-page": { |
| 73 | "name": "@lapin-fute/config-page", | 73 | "name": "@lapin-fute/config-page", |
| 74 | - "version": "1.2.3", | 74 | + "version": "1.2.4", |
| 75 | "license": "MIT" | 75 | "license": "MIT" |
| 76 | }, | 76 | }, |
| 77 | "packages/watch": { | 77 | "packages/watch": { |
| 78 | "name": "lapin-fute-watch", | 78 | "name": "lapin-fute-watch", |
| 79 | - "version": "1.2.3", | 79 | + "version": "1.2.4", |
| 80 | "license": "MIT" | 80 | "license": "MIT" |
| 81 | } | 81 | } |
| 82 | } | 82 | } |
modified
package.json +1 -1 | @@ -1,6 +1,6 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "name": "lapin-fute", |
| 3 | - "version": "1.2.3", | |
| 3 | + "version": "1.2.4", | |
| 4 | 4 | "license": "MIT", |
| 5 | 5 | "private": true, |
| 6 | 6 | "type": "module", |
| @@ -1,6 +1,6 @@ | |||
| 1 | { | 1 | { |
| 2 | "name": "lapin-fute", | 2 | "name": "lapin-fute", |
| 3 | - "version": "1.2.3", | 3 | + "version": "1.2.4", |
| 4 | "license": "MIT", | 4 | "license": "MIT", |
| 5 | "private": true, | 5 | "private": true, |
| 6 | "type": "module", | 6 | "type": "module", |
modified
packages/catalog/package.json +1 -1 | @@ -1,6 +1,6 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "name": "@lapin-fute/catalog", |
| 3 | - "version": "1.2.3", | |
| 3 | + "version": "1.2.4", | |
| 4 | 4 | "license": "MIT", |
| 5 | 5 | "private": true, |
| 6 | 6 | "type": "module", |
| @@ -1,6 +1,6 @@ | |||
| 1 | { | 1 | { |
| 2 | "name": "@lapin-fute/catalog", | 2 | "name": "@lapin-fute/catalog", |
| 3 | - "version": "1.2.3", | 3 | + "version": "1.2.4", |
| 4 | "license": "MIT", | 4 | "license": "MIT", |
| 5 | "private": true, | 5 | "private": true, |
| 6 | "type": "module", | 6 | "type": "module", |
modified
packages/companion/package.json +1 -1 | @@ -1,6 +1,6 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "name": "@lapin-fute/companion", |
| 3 | - "version": "1.2.3", | |
| 3 | + "version": "1.2.4", | |
| 4 | 4 | "license": "MIT", |
| 5 | 5 | "private": true, |
| 6 | 6 | "type": "commonjs", |
| @@ -1,6 +1,6 @@ | |||
| 1 | { | 1 | { |
| 2 | "name": "@lapin-fute/companion", | 2 | "name": "@lapin-fute/companion", |
| 3 | - "version": "1.2.3", | 3 | + "version": "1.2.4", |
| 4 | "license": "MIT", | 4 | "license": "MIT", |
| 5 | "private": true, | 5 | "private": true, |
| 6 | "type": "commonjs", | 6 | "type": "commonjs", |
modified
packages/config-page/index.html +2 -2 | @@ -175,7 +175,7 @@ | ||
| 175 | 175 | </div> |
| 176 | 176 | |
| 177 | 177 | <div id="about-en" lang="en"> |
| 178 | - <p class="about-version">Version 1.2.3</p> | |
| 178 | + <p class="about-version">Version 1.2.4</p> | |
| 179 | 179 | <h3>Publisher and contact</h3> |
| 180 | 180 | <p><strong>Author and publisher:</strong> Anthodev, a private individual acting on a non-professional basis.</p> |
| 181 | 181 | <p><strong>Publication director:</strong> Anthodev.</p> |
| @@ -197,7 +197,7 @@ | ||
| 197 | 197 | </div> |
| 198 | 198 | |
| 199 | 199 | <div id="about-fr" lang="fr" hidden> |
| 200 | - <p class="about-version">Version 1.2.3</p> | |
| 200 | + <p class="about-version">Version 1.2.4</p> | |
| 201 | 201 | <h3>Édition et contact</h3> |
| 202 | 202 | <p><strong>Auteur et éditeur :</strong> Anthodev, personne physique agissant à titre privé et non professionnel.</p> |
| 203 | 203 | <p><strong>Directeur de la publication :</strong> Anthodev.</p> |
| @@ -175,7 +175,7 @@ | |||
| 175 | </div> | 175 | </div> |
| 176 | 176 | ||
| 177 | <div id="about-en" lang="en"> | 177 | <div id="about-en" lang="en"> |
| 178 | - <p class="about-version">Version 1.2.3</p> | 178 | + <p class="about-version">Version 1.2.4</p> |
| 179 | <h3>Publisher and contact</h3> | 179 | <h3>Publisher and contact</h3> |
| 180 | <p><strong>Author and publisher:</strong> Anthodev, a private individual acting on a non-professional basis.</p> | 180 | <p><strong>Author and publisher:</strong> Anthodev, a private individual acting on a non-professional basis.</p> |
| 181 | <p><strong>Publication director:</strong> Anthodev.</p> | 181 | <p><strong>Publication director:</strong> Anthodev.</p> |
| @@ -197,7 +197,7 @@ | |||
| 197 | </div> | 197 | </div> |
| 198 | 198 | ||
| 199 | <div id="about-fr" lang="fr" hidden> | 199 | <div id="about-fr" lang="fr" hidden> |
| 200 | - <p class="about-version">Version 1.2.3</p> | 200 | + <p class="about-version">Version 1.2.4</p> |
| 201 | <h3>Édition et contact</h3> | 201 | <h3>Édition et contact</h3> |
| 202 | <p><strong>Auteur et éditeur :</strong> Anthodev, personne physique agissant à titre privé et non professionnel.</p> | 202 | <p><strong>Auteur et éditeur :</strong> Anthodev, personne physique agissant à titre privé et non professionnel.</p> |
| 203 | <p><strong>Directeur de la publication :</strong> Anthodev.</p> | 203 | <p><strong>Directeur de la publication :</strong> Anthodev.</p> |
modified
packages/config-page/package.json +1 -1 | @@ -1,6 +1,6 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "name": "@lapin-fute/config-page", |
| 3 | - "version": "1.2.3", | |
| 3 | + "version": "1.2.4", | |
| 4 | 4 | "license": "MIT", |
| 5 | 5 | "private": true, |
| 6 | 6 | "type": "module", |
| @@ -1,6 +1,6 @@ | |||
| 1 | { | 1 | { |
| 2 | "name": "@lapin-fute/config-page", | 2 | "name": "@lapin-fute/config-page", |
| 3 | - "version": "1.2.3", | 3 | + "version": "1.2.4", |
| 4 | "license": "MIT", | 4 | "license": "MIT", |
| 5 | "private": true, | 5 | "private": true, |
| 6 | "type": "module", | 6 | "type": "module", |
modified
packages/watch/package.json +1 -1 | @@ -1,7 +1,7 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "name": "lapin-fute-watch", |
| 3 | 3 | "author": "Anthodev", |
| 4 | - "version": "1.2.3", | |
| 4 | + "version": "1.2.4", | |
| 5 | 5 | "license": "MIT", |
| 6 | 6 | "keywords": [ |
| 7 | 7 | "pebble-app" |
| @@ -1,7 +1,7 @@ | |||
| 1 | { | 1 | { |
| 2 | "name": "lapin-fute-watch", | 2 | "name": "lapin-fute-watch", |
| 3 | "author": "Anthodev", | 3 | "author": "Anthodev", |
| 4 | - "version": "1.2.3", | 4 | + "version": "1.2.4", |
| 5 | "license": "MIT", | 5 | "license": "MIT", |
| 6 | "keywords": [ | 6 | "keywords": [ |
| 7 | "pebble-app" | 7 | "pebble-app" |