diff --git a/.github/actions/cloudflare-upload/action.yml b/.github/actions/cloudflare-upload/action.yml deleted file mode 100644 index ceac755bb3..0000000000 --- a/.github/actions/cloudflare-upload/action.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: "Upload an app to Cloudflare Pages" -description: "Manually deploy an app to cloudflare pages" -inputs: - upload-dir: - description: "The name of the app to build and export" - required: true - - project-name: - description: "The name of the project to upload to" - required: true - - cloudflare-account: - description: "The Cloudflare account ID" - required: true - - cloudflare-api-token: - description: "The Cloudflare API token" - required: true - -runs: - using: composite - steps: - - name: Find PR Preview Comment - if: github.event_name == 'pull_request' - uses: peter-evans/find-comment@v1 - id: deploy-preview-comment - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: "github-actions[bot]" - body-includes: ${{ inputs.project-name }} - - - name: Update Comment if exists - if: github.event_name == 'pull_request' && steps.deploy-preview-comment.outputs.comment-id != 0 - uses: peter-evans/create-or-update-comment@v1.4.5 - with: - comment-id: ${{ steps.deploy-preview-comment.outputs.comment-id }} - edit-mode: replace - body: | - ### ${{ inputs.project-name }} - Waiting for deployment to complete... - - - id: branch-name - uses: tj-actions/branch-names@v5 - - - id: format-branch - shell: bash - run: format=$(echo ${{ steps.branch-name.outputs.current_branch }} | tr / -) && echo "::set-output name=branch::$(echo "${format:0:28}")" - - - name: deploy-cloudflare - uses: cloudflare/pages-action@v1 - id: deploy-cloudflare - with: - apiToken: ${{ inputs.cloudflare-api-token }} - accountId: ${{ inputs.cloudflare-account }} - projectName: ${{ inputs.project-name }} - directory: ${{ inputs.upload-dir }} - branch: ${{ steps.format-branch.outputs.branch }} - - - name: Create PR Preview Comment - if: github.event_name == 'pull_request' && steps.deploy-preview-comment.outputs.comment-id == 0 - uses: peter-evans/create-or-update-comment@v1.4.5 - with: - issue-number: ${{ github.event.pull_request.number }} - body: | - ### ${{ inputs.project-name }} - 🚀 Preview this PR: ${{ steps.deploy-cloudflare.outputs.url }} - 📍 Commit SHA: ${{ github.sha }} - - - name: Update PR Preview Comment - if: github.event_name == 'pull_request' && steps.deploy-preview-comment.outputs.comment-id != 0 - uses: peter-evans/create-or-update-comment@v1.4.5 - with: - comment-id: ${{ steps.deploy-preview-comment.outputs.comment-id }} - edit-mode: replace - body: | - ### ${{ inputs.project-name }} - 🚀 Preview this PR: ${{ steps.deploy-cloudflare.outputs.url }} - 📍 Commit SHA: ${{ github.sha }} diff --git a/.github/actions/generate-datasheets/action.yml b/.github/actions/generate-datasheets/action.yml index 908c4f6d10..60627bea9c 100644 --- a/.github/actions/generate-datasheets/action.yml +++ b/.github/actions/generate-datasheets/action.yml @@ -12,16 +12,16 @@ inputs: runs: using: composite steps: - - uses: actions/cache@v4 + - uses: actions/cache@v5 id: cache with: path: ${{ inputs.datasheets-path }} key: ${{ runner.os }}-datasheets-${{ hashFiles('**/*datasheet.md') }} - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 if: steps.cache.outputs.cache-hit != 'true' with: - node-version: 18 + node-version: 22 cache: "npm" cache-dependency-path: "package-lock.json" @@ -36,7 +36,7 @@ runs: shell: bash - name: Export artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ${{ inputs.artifact-name }} path: ${{ inputs.datasheets-path }} diff --git a/.github/actions/image-size-check/action.yml b/.github/actions/image-size-check/action.yml index cba3fd7795..dd6d7f039a 100644 --- a/.github/actions/image-size-check/action.yml +++ b/.github/actions/image-size-check/action.yml @@ -5,7 +5,9 @@ runs: steps: - name: Get changed files id: changed-files - uses: tj-actions/changed-files@v44 + # Pinned to a commit SHA rather than a tag: this action had all of its tags + # rewritten to point at malicious code in the March 2025 compromise (CVE-2025-30066). + uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6 with: separator: '\n' files_ignore_pattern: | diff --git a/.github/actions/sync-s3/action.yml b/.github/actions/sync-s3/action.yml index 7f2a7524ac..4493a7a60a 100644 --- a/.github/actions/sync-s3/action.yml +++ b/.github/actions/sync-s3/action.yml @@ -11,7 +11,7 @@ runs: using: composite steps: - name: Configure AWS credentials from Staging account - uses: aws-actions/configure-aws-credentials@v4 + uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: ${{ inputs.role-to-assume }} aws-region: us-east-1 diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index 88625b6fa1..a1f3655358 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -21,8 +21,44 @@ jobs: render-datasheets: name: Render Datasheets runs-on: ubuntu-latest + timeout-minutes: 30 steps: - - uses: actions/checkout@v4 + # Checkout stalls intermittently on this repo (~9 GB): on 2026-08-25, 4 of + # 8 checkouts hung with no progress at all. Bound the attempt and retry + # once, instead of letting a stalled transfer run into the 6h default. + - uses: actions/checkout@v5 + id: checkout + continue-on-error: true + timeout-minutes: 5 + with: + fetch-depth: 1 + + # A checkout killed by the timeout leaves a partial clone behind (and + # sometimes a stale index.lock): wiping the workspace makes the second + # attempt genuinely independent of the first. + - name: Reset workspace before checkout retry + if: steps.checkout.outcome == 'failure' + run: | + shopt -s dotglob + rm -rf "${GITHUB_WORKSPACE:?}"/* + + - uses: actions/checkout@v5 + id: checkout-retry + if: steps.checkout.outcome == 'failure' + continue-on-error: true + timeout-minutes: 5 + with: + fetch-depth: 1 + + # Attempt 2 has failed outright before, not only stalled: one more wipe and a last try, bounded by the job timeout + - name: Reset workspace before last checkout attempt + if: steps.checkout.outcome == 'failure' && steps.checkout-retry.outcome == 'failure' + run: | + shopt -s dotglob + rm -rf "${GITHUB_WORKSPACE:?}"/* + + - uses: actions/checkout@v5 + if: steps.checkout.outcome == 'failure' && steps.checkout-retry.outcome == 'failure' with: fetch-depth: 1 @@ -36,26 +72,69 @@ jobs: needs: render-datasheets runs-on: ubuntu-latest environment: production + timeout-minutes: 150 env: REPO_ACCESS_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }} APP_ENV: prod steps: - - uses: actions/checkout@v4 + # fetch-depth: 0 is required by gatsby-transformer-gitinfo, which runs + # `git log -n 1 -- ` on every content file to populate + # gitLogLatestDate (the "last updated" date shown on tutorial pages). + # filter: blob:none still fetches every commit and every tree - so that + # git log keeps resolving locally - while skipping historical blob + # contents, which are the bulk of this repo's ~11 GB of git history. + # Do NOT swap this for fetch-depth: 1: it would blank out gitLogLatestDate. + # Checkout stalls intermittently on this repo (~9 GB): on 2026-08-25, 4 of + # 8 checkouts hung with no progress at all. Bound the attempt and retry + # once, instead of letting a stalled transfer run into the 6h default. + - uses: actions/checkout@v5 + id: checkout + continue-on-error: true + timeout-minutes: 5 with: fetch-depth: "0" + filter: blob:none - - name: Cleanup runner disk - uses: ./.github/actions/cleanup-disk # Cleanup machine before starting the build + # A checkout killed by the timeout leaves a partial clone behind (and + # sometimes a stale index.lock): wiping the workspace makes the second + # attempt genuinely independent of the first. + - name: Reset workspace before checkout retry + if: steps.checkout.outcome == 'failure' + run: | + shopt -s dotglob + rm -rf "${GITHUB_WORKSPACE:?}"/* - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + id: checkout-retry + if: steps.checkout.outcome == 'failure' + continue-on-error: true + timeout-minutes: 5 with: - node-version: 18 + fetch-depth: "0" + filter: blob:none + + # Attempt 2 has failed outright before, not only stalled: one more wipe and a last try, bounded by the job timeout + - name: Reset workspace before last checkout attempt + if: steps.checkout.outcome == 'failure' && steps.checkout-retry.outcome == 'failure' + run: | + shopt -s dotglob + rm -rf "${GITHUB_WORKSPACE:?}"/* + + - uses: actions/checkout@v5 + if: steps.checkout.outcome == 'failure' && steps.checkout-retry.outcome == 'failure' + with: + fetch-depth: "0" + filter: blob:none + + - uses: actions/setup-node@v5 + with: + node-version: 22 cache: "npm" cache-dependency-path: "package-lock.json" - name: Retrieve Datasheets - uses: actions/download-artifact@v4 # Retrieve the datasheets generated in the previous job + uses: actions/download-artifact@v8 # Retrieve the datasheets generated in the previous job with: name: datasheets path: static/resources/datasheets @@ -117,36 +196,84 @@ jobs: # echo "No oversized files found. New check passed." # fi + # Key on the git blob hash of each file, not its size: two different images of equal weight would share a key + - name: Compressed images key + id: images-key + run: | + find content -type f \( -name '*.jpg' -o -name '*.jpeg' -o -name '*.png' \) -size +2M -print0 > /tmp/big-images.lst + xargs -0 -r git ls-files -s -- < /tmp/big-images.lst > /tmp/big-images.idx + # An untracked image would be compressed but absent from the key, so require one index line per file found + test "$(tr -dc '\0' < /tmp/big-images.lst | wc -c)" -eq "$(wc -l < /tmp/big-images.idx)" + echo "hash=$(sort /tmp/big-images.idx | sha256sum | cut -c1-40)" >> "$GITHUB_OUTPUT" + + - name: Compressed images cache + uses: actions/cache@v5 + with: + path: /tmp/compressed-images.tar + key: ${{ runner.os }}-compressed-images-${{ steps.images-key.outputs.hash }} + - name: Compress Large Images run: | + # jpegoptim and optipng are deterministic, so a hit means the same bytes without spending ~9 min again + if [ -f /tmp/compressed-images.tar ]; then + echo "Restoring images compressed by an earlier run..." + tar xf /tmp/compressed-images.tar + exit 0 + fi sudo apt-get update && sudo apt-get install -y jpegoptim optipng + # A missing compressor is a systemic failure and must stop the job, unlike the per-file errors tolerated below + command -v jpegoptim > /dev/null && command -v optipng > /dev/null + # -exec ... \; spawns one process per file on a single core: xargs -P "$(nproc)" spreads the same work over every core + find content -type f \( -name "*.jpg" -o -name "*.jpeg" \) -size +2M -print0 > /tmp/big-jpg.lst + find content -type f -name "*.png" -size +2M -print0 > /tmp/big-png.lst + # find -exec ignored the exit status of each command, xargs reports 123 if any file fails: a few .png are really JPEGs echo "Compressing large JPGs..." - find content -type f \( -name "*.jpg" -o -name "*.jpeg" \) -size +2M -print -exec jpegoptim --strip-all {} \; + xargs -0 -r -P "$(nproc)" -n 1 jpegoptim --strip-all < /tmp/big-jpg.lst || echo "Some JPGs could not be optimized, continuing" echo "Compressing large PNGs..." - find content -type f -name "*.png" -size +2M -print -exec optipng -o2 {} \; + xargs -0 -r -P "$(nproc)" -n 1 optipng -o2 < /tmp/big-png.lst || echo "Some PNGs could not be optimized, continuing" + # The list is captured before compressing: some files drop under 2M and find would no longer report them + cat /tmp/big-jpg.lst /tmp/big-png.lst | tar --null -cf /tmp/compressed-images.tar -T - - name: Gatsby main cache - uses: actions/cache@v4 + uses: actions/cache/restore@v5 id: gatsby-cache-folder with: path: .cache - key: ${{ runner.os }}-cache-gatsby-${{ github.ref_name }} + key: ${{ runner.os }}-cache-gatsby-node22-${{ github.ref_name }} restore-keys: | - ${{ runner.os }}-cache-gatsby-main + ${{ runner.os }}-cache-gatsby-node22-main - name: Gatsby Public Folder cache - uses: actions/cache@v4 + uses: actions/cache/restore@v5 id: gatsby-public-folder with: path: public/ - key: ${{ runner.os }}-public-gatsby-${{ github.ref_name }} + key: ${{ runner.os }}-public-gatsby-node22-${{ github.ref_name }} restore-keys: | - ${{ runner.os }}-public-gatsby-main + ${{ runner.os }}-public-gatsby-node22-main - - run: npm install + - run: npm install --prefer-offline --no-audit --fund=false - name: Build run: npm run build + env: + # Gatsby defaults to physical cores, which is half the vCPUs of a runner + GATSBY_CPU_COUNT: logical_cores + + # A warm public/ is worth 38 minutes of sharp and ~4 GB of quota: only a run on main writes the entry every branch reads + - name: Save Gatsby main cache + if: github.ref_name == 'main' + uses: actions/cache/save@v5 + with: + path: .cache + key: ${{ runner.os }}-cache-gatsby-node22-main-${{ github.run_id }} + + - name: Save Gatsby Public Folder cache + if: github.ref_name == 'main' + uses: actions/cache/save@v5 + with: + path: public/ + key: ${{ runner.os }}-public-gatsby-node22-main-${{ github.run_id }} - name: Clean up node_modules # Just to save space run: rm -rf node_modules @@ -162,6 +289,7 @@ jobs: needs: build runs-on: ubuntu-latest environment: production + timeout-minutes: 10 steps: - name: Purge Cloudflare Cache shell: bash diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index 190f405b1d..b3e5b05d7c 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -21,8 +21,44 @@ jobs: render-datasheets: name: Render Datasheets runs-on: ubuntu-latest + timeout-minutes: 30 steps: - - uses: actions/checkout@v4 + # Checkout stalls intermittently on this repo (~9 GB): on 2026-08-25, 4 of + # 8 checkouts hung with no progress at all. Bound the attempt and retry + # once, instead of letting a stalled transfer run into the 6h default. + - uses: actions/checkout@v5 + id: checkout + continue-on-error: true + timeout-minutes: 5 + with: + fetch-depth: 1 + + # A checkout killed by the timeout leaves a partial clone behind (and + # sometimes a stale index.lock): wiping the workspace makes the second + # attempt genuinely independent of the first. + - name: Reset workspace before checkout retry + if: steps.checkout.outcome == 'failure' + run: | + shopt -s dotglob + rm -rf "${GITHUB_WORKSPACE:?}"/* + + - uses: actions/checkout@v5 + id: checkout-retry + if: steps.checkout.outcome == 'failure' + continue-on-error: true + timeout-minutes: 5 + with: + fetch-depth: 1 + + # Attempt 2 has failed outright before, not only stalled: one more wipe and a last try, bounded by the job timeout + - name: Reset workspace before last checkout attempt + if: steps.checkout.outcome == 'failure' && steps.checkout-retry.outcome == 'failure' + run: | + shopt -s dotglob + rm -rf "${GITHUB_WORKSPACE:?}"/* + + - uses: actions/checkout@v5 + if: steps.checkout.outcome == 'failure' && steps.checkout-retry.outcome == 'failure' with: fetch-depth: 1 @@ -36,26 +72,69 @@ jobs: needs: render-datasheets runs-on: ubuntu-latest environment: staging + timeout-minutes: 150 env: REPO_ACCESS_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }} APP_ENV: staging steps: - - uses: actions/checkout@v4 + # fetch-depth: 0 is required by gatsby-transformer-gitinfo, which runs + # `git log -n 1 -- ` on every content file to populate + # gitLogLatestDate (the "last updated" date shown on tutorial pages). + # filter: blob:none still fetches every commit and every tree - so that + # git log keeps resolving locally - while skipping historical blob + # contents, which are the bulk of this repo's ~11 GB of git history. + # Do NOT swap this for fetch-depth: 1: it would blank out gitLogLatestDate. + # Checkout stalls intermittently on this repo (~9 GB): on 2026-08-25, 4 of + # 8 checkouts hung with no progress at all. Bound the attempt and retry + # once, instead of letting a stalled transfer run into the 6h default. + - uses: actions/checkout@v5 + id: checkout + continue-on-error: true + timeout-minutes: 5 with: fetch-depth: "0" + filter: blob:none - - name: Cleanup runner disk - uses: ./.github/actions/cleanup-disk # Cleanup machine before starting the build + # A checkout killed by the timeout leaves a partial clone behind (and + # sometimes a stale index.lock): wiping the workspace makes the second + # attempt genuinely independent of the first. + - name: Reset workspace before checkout retry + if: steps.checkout.outcome == 'failure' + run: | + shopt -s dotglob + rm -rf "${GITHUB_WORKSPACE:?}"/* - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + id: checkout-retry + if: steps.checkout.outcome == 'failure' + continue-on-error: true + timeout-minutes: 5 with: - node-version: 18 + fetch-depth: "0" + filter: blob:none + + # Attempt 2 has failed outright before, not only stalled: one more wipe and a last try, bounded by the job timeout + - name: Reset workspace before last checkout attempt + if: steps.checkout.outcome == 'failure' && steps.checkout-retry.outcome == 'failure' + run: | + shopt -s dotglob + rm -rf "${GITHUB_WORKSPACE:?}"/* + + - uses: actions/checkout@v5 + if: steps.checkout.outcome == 'failure' && steps.checkout-retry.outcome == 'failure' + with: + fetch-depth: "0" + filter: blob:none + + - uses: actions/setup-node@v5 + with: + node-version: 22 cache: "npm" cache-dependency-path: "package-lock.json" - name: Retrieve Datasheets - uses: actions/download-artifact@v4 # Retrieve the datasheets generated in the previous job + uses: actions/download-artifact@v8 # Retrieve the datasheets generated in the previous job with: name: datasheets path: static/resources/datasheets @@ -117,36 +196,76 @@ jobs: # echo "No oversized files found. New check passed." # fi + # Key on the git blob hash of each file, not its size: two different images of equal weight would share a key + - name: Compressed images key + id: images-key + run: | + find content -type f \( -name '*.jpg' -o -name '*.jpeg' -o -name '*.png' \) -size +2M -print0 > /tmp/big-images.lst + xargs -0 -r git ls-files -s -- < /tmp/big-images.lst > /tmp/big-images.idx + # An untracked image would be compressed but absent from the key, so require one index line per file found + test "$(tr -dc '\0' < /tmp/big-images.lst | wc -c)" -eq "$(wc -l < /tmp/big-images.idx)" + echo "hash=$(sort /tmp/big-images.idx | sha256sum | cut -c1-40)" >> "$GITHUB_OUTPUT" + + - name: Compressed images cache + uses: actions/cache@v5 + with: + path: /tmp/compressed-images.tar + key: ${{ runner.os }}-compressed-images-${{ steps.images-key.outputs.hash }} + - name: Compress Large Images run: | + # jpegoptim and optipng are deterministic, so a hit means the same bytes without spending ~9 min again + if [ -f /tmp/compressed-images.tar ]; then + echo "Restoring images compressed by an earlier run..." + tar xf /tmp/compressed-images.tar + exit 0 + fi sudo apt-get update && sudo apt-get install -y jpegoptim optipng + # A missing compressor is a systemic failure and must stop the job, unlike the per-file errors tolerated below + command -v jpegoptim > /dev/null && command -v optipng > /dev/null + # -exec ... \; spawns one process per file on a single core: xargs -P "$(nproc)" spreads the same work over every core + find content -type f \( -name "*.jpg" -o -name "*.jpeg" \) -size +2M -print0 > /tmp/big-jpg.lst + find content -type f -name "*.png" -size +2M -print0 > /tmp/big-png.lst + # find -exec ignored the exit status of each command, xargs reports 123 if any file fails: a few .png are really JPEGs echo "Compressing large JPGs..." - find content -type f \( -name "*.jpg" -o -name "*.jpeg" \) -size +2M -print -exec jpegoptim --strip-all {} \; + xargs -0 -r -P "$(nproc)" -n 1 jpegoptim --strip-all < /tmp/big-jpg.lst || echo "Some JPGs could not be optimized, continuing" echo "Compressing large PNGs..." - find content -type f -name "*.png" -size +2M -print -exec optipng -o2 {} \; + xargs -0 -r -P "$(nproc)" -n 1 optipng -o2 < /tmp/big-png.lst || echo "Some PNGs could not be optimized, continuing" + # The list is captured before compressing: some files drop under 2M and find would no longer report them + cat /tmp/big-jpg.lst /tmp/big-png.lst | tar --null -cf /tmp/compressed-images.tar -T - + # Staging runs one branch at a time, so it keeps its own entries; the quota was drained by per-PR previews, not by these - name: Gatsby main cache - uses: actions/cache@v4 + uses: actions/cache@v5 id: gatsby-cache-folder with: path: .cache - key: ${{ runner.os }}-cache-gatsby-${{ github.ref_name }} + key: ${{ runner.os }}-cache-gatsby-node22-${{ github.ref_name }} restore-keys: | - ${{ runner.os }}-cache-gatsby-main + ${{ runner.os }}-cache-gatsby-node22-main - name: Gatsby Public Folder cache - uses: actions/cache@v4 + uses: actions/cache@v5 id: gatsby-public-folder with: path: public/ - key: ${{ runner.os }}-public-gatsby-${{ github.ref_name }} + key: ${{ runner.os }}-public-gatsby-node22-${{ github.ref_name }} restore-keys: | - ${{ runner.os }}-public-gatsby-main + ${{ runner.os }}-public-gatsby-node22-main + + - run: npm install --prefer-offline --no-audit --fund=false - - run: npm install + # nproc and free tell us whether logical_cores actually doubled the workers, and how much headroom they have + - name: Runner capacity + run: | + echo "vCPU: $(nproc)" + free -g - name: Build run: npm run build + env: + # Gatsby defaults to physical cores, which is half the vCPUs of a runner + GATSBY_CPU_COUNT: logical_cores - name: Clean up node_modules # Just to save space run: rm -rf node_modules @@ -162,6 +281,7 @@ jobs: needs: build runs-on: ubuntu-latest environment: staging + timeout-minutes: 10 steps: - name: Purge Cloudflare Cache shell: bash diff --git a/.github/workflows/docs-auto-bump.yml b/.github/workflows/docs-auto-bump.yml index d2c222c639..5f54a859ed 100644 --- a/.github/workflows/docs-auto-bump.yml +++ b/.github/workflows/docs-auto-bump.yml @@ -15,12 +15,12 @@ jobs: REPO_ACCESS_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }} steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v5 with: - node-version: 20 + node-version: 22 cache: 'npm' - name: Configure Git @@ -54,7 +54,7 @@ jobs: run: npm install --ignore-scripts - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v8 with: token: ${{ secrets.BUMP_DOCS_TOKEN }} commit-message: 'Bump @arduino/docs.arduino.cc to v${{ steps.get_version.outputs.version }}' diff --git a/.github/workflows/image-size-check.yml b/.github/workflows/image-size-check.yml index e58ea09d8e..473c60c324 100644 --- a/.github/workflows/image-size-check.yml +++ b/.github/workflows/image-size-check.yml @@ -13,7 +13,7 @@ jobs: # every historical version of the PDFs and ZIPs: those blobs are ~10GB of the # 10.4GB pack and they filled the runner disk, so the checkout never completed. - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 filter: blob:none diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 87b5e7fa6f..c34b842bd4 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -18,8 +18,44 @@ jobs: name: Render Datasheets if: ${{ contains(github.event.pull_request.labels.*.name, 'preview') || github.ref_name == 'main' }} runs-on: ubuntu-latest + timeout-minutes: 30 steps: - - uses: actions/checkout@v4 + # Checkout stalls intermittently on this repo (~9 GB): on 2026-08-25, 4 of + # 8 checkouts hung with no progress at all. Bound the attempt and retry + # once, instead of letting a stalled transfer run into the 6h default. + - uses: actions/checkout@v5 + id: checkout + continue-on-error: true + timeout-minutes: 5 + with: + fetch-depth: 1 + + # A checkout killed by the timeout leaves a partial clone behind (and + # sometimes a stale index.lock): wiping the workspace makes the second + # attempt genuinely independent of the first. + - name: Reset workspace before checkout retry + if: steps.checkout.outcome == 'failure' + run: | + shopt -s dotglob + rm -rf "${GITHUB_WORKSPACE:?}"/* + + - uses: actions/checkout@v5 + id: checkout-retry + if: steps.checkout.outcome == 'failure' + continue-on-error: true + timeout-minutes: 5 + with: + fetch-depth: 1 + + # Attempt 2 has failed outright before, not only stalled: one more wipe and a last try, bounded by the job timeout + - name: Reset workspace before last checkout attempt + if: steps.checkout.outcome == 'failure' && steps.checkout-retry.outcome == 'failure' + run: | + shopt -s dotglob + rm -rf "${GITHUB_WORKSPACE:?}"/* + + - uses: actions/checkout@v5 + if: steps.checkout.outcome == 'failure' && steps.checkout-retry.outcome == 'failure' with: fetch-depth: 1 @@ -32,6 +68,7 @@ jobs: if: ${{ contains(github.event.pull_request.labels.*.name, 'preview') || github.ref_name == 'main' }} runs-on: ubuntu-latest needs: render-datasheets + timeout-minutes: 150 concurrency: group: netlify cancel-in-progress: false @@ -44,7 +81,7 @@ jobs: - name: Find PR Preview Comment if: github.event_name == 'pull_request' - uses: peter-evans/find-comment@v1 + uses: peter-evans/find-comment@v4 id: deploy-preview-comment with: issue-number: ${{ github.event.pull_request.number }} @@ -53,7 +90,7 @@ jobs: - name: Update Comment if exists if: github.event_name == 'pull_request' && steps.deploy-preview-comment.outputs.comment-id != 0 - uses: peter-evans/create-or-update-comment@v1.4.5 + uses: peter-evans/create-or-update-comment@v5 with: comment-id: ${{ steps.deploy-preview-comment.outputs.comment-id }} edit-mode: replace @@ -61,20 +98,53 @@ jobs: ## Preview Deployment Waiting for deployment to complete... - - uses: actions/checkout@v4 + # Checkout stalls intermittently on this repo (~9 GB): on 2026-08-25, 4 of + # 8 checkouts hung with no progress at all. Bound the attempt and retry + # once, instead of letting a stalled transfer run into the 6h default. + - uses: actions/checkout@v5 + id: checkout + continue-on-error: true + timeout-minutes: 5 + with: + fetch-depth: 1 + + # A checkout killed by the timeout leaves a partial clone behind (and + # sometimes a stale index.lock): wiping the workspace makes the second + # attempt genuinely independent of the first. + - name: Reset workspace before checkout retry + if: steps.checkout.outcome == 'failure' + run: | + shopt -s dotglob + rm -rf "${GITHUB_WORKSPACE:?}"/* + + - uses: actions/checkout@v5 + id: checkout-retry + if: steps.checkout.outcome == 'failure' + continue-on-error: true + timeout-minutes: 5 + with: + fetch-depth: 1 + + # Attempt 2 has failed outright before, not only stalled: one more wipe and a last try, bounded by the job timeout + - name: Reset workspace before last checkout attempt + if: steps.checkout.outcome == 'failure' && steps.checkout-retry.outcome == 'failure' + run: | + shopt -s dotglob + rm -rf "${GITHUB_WORKSPACE:?}"/* + + - uses: actions/checkout@v5 + if: steps.checkout.outcome == 'failure' && steps.checkout-retry.outcome == 'failure' with: fetch-depth: 1 - - name: Cleanup runner disk - uses: ./.github/actions/cleanup-disk - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: - node-version: 18 + node-version: 22 cache: "npm" cache-dependency-path: "**/package-lock.json" - name: Retrieve Datasheets - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: datasheets path: static/resources/datasheets @@ -120,34 +190,69 @@ jobs: # echo "No oversized files found. New check passed." # fi + # Key on the git blob hash of each file, not its size: two different images of equal weight would share a key + - name: Compressed images key + id: images-key + run: | + find content -type f \( -name '*.jpg' -o -name '*.jpeg' -o -name '*.png' \) -size +2M -print0 > /tmp/big-images.lst + xargs -0 -r git ls-files -s -- < /tmp/big-images.lst > /tmp/big-images.idx + # An untracked image would be compressed but absent from the key, so require one index line per file found + test "$(tr -dc '\0' < /tmp/big-images.lst | wc -c)" -eq "$(wc -l < /tmp/big-images.idx)" + echo "hash=$(sort /tmp/big-images.idx | sha256sum | cut -c1-40)" >> "$GITHUB_OUTPUT" + + - name: Compressed images cache + uses: actions/cache@v5 + with: + path: /tmp/compressed-images.tar + key: ${{ runner.os }}-compressed-images-${{ steps.images-key.outputs.hash }} + - name: Compress Large Images run: | + # jpegoptim and optipng are deterministic, so a hit means the same bytes without spending ~9 min again + if [ -f /tmp/compressed-images.tar ]; then + echo "Restoring images compressed by an earlier run..." + tar xf /tmp/compressed-images.tar + exit 0 + fi sudo apt-get update && sudo apt-get install -y jpegoptim optipng + # A missing compressor is a systemic failure and must stop the job, unlike the per-file errors tolerated below + command -v jpegoptim > /dev/null && command -v optipng > /dev/null + # -exec ... \; spawns one process per file on a single core: xargs -P "$(nproc)" spreads the same work over every core + find content -type f \( -name "*.jpg" -o -name "*.jpeg" \) -size +2M -print0 > /tmp/big-jpg.lst + find content -type f -name "*.png" -size +2M -print0 > /tmp/big-png.lst + # find -exec ignored the exit status of each command, xargs reports 123 if any file fails: a few .png are really JPEGs echo "Compressing large JPGs..." - find content -type f \( -name "*.jpg" -o -name "*.jpeg" \) -size +2M -print -exec jpegoptim --strip-all {} \; + xargs -0 -r -P "$(nproc)" -n 1 jpegoptim --strip-all < /tmp/big-jpg.lst || echo "Some JPGs could not be optimized, continuing" echo "Compressing large PNGs..." - find content -type f -name "*.png" -size +2M -print -exec optipng -o2 {} \; + xargs -0 -r -P "$(nproc)" -n 1 optipng -o2 < /tmp/big-png.lst || echo "Some PNGs could not be optimized, continuing" + # The list is captured before compressing: some files drop under 2M and find would no longer report them + cat /tmp/big-jpg.lst /tmp/big-png.lst | tar --null -cf /tmp/compressed-images.tar -T - + # .cache is ~0.1 GB, so every PR can keep its own across pushes - name: Gatsby main cache - uses: actions/cache@v4 + uses: actions/cache@v5 id: gatsby-cache-folder with: path: .cache - key: ${{ runner.os }}-cache-gatsby-${{ github.ref_name }} + key: ${{ runner.os }}-cache-gatsby-node22-${{ github.ref_name }} restore-keys: | - ${{ runner.os }}-cache-gatsby-main + ${{ runner.os }}-cache-gatsby-node22-main + # public/ is ~3.9 GB: one entry per PR filled the 10 GB quota and evicted main's, which is the one every PR reads - name: Gatsby Public Folder - uses: actions/cache@v4 + uses: actions/cache/restore@v5 id: gatsby-public-folder with: path: public/ - key: ${{ runner.os }}-public-gatsby-${{ github.ref_name }} + key: ${{ runner.os }}-public-gatsby-node22-${{ github.ref_name }} restore-keys: | - ${{ runner.os }}-public-gatsby-main + ${{ runner.os }}-public-gatsby-node22-main - - run: npm install + - run: npm install --prefer-offline --no-audit --fund=false - run: GATSBY_HF_CDN_URL="" npm run build + env: + # Gatsby defaults to physical cores, which is half the vCPUs of a runner + GATSBY_CPU_COUNT: logical_cores - name: Install Netlify run: npm install netlify-cli@17.23.2 -g @@ -182,7 +287,7 @@ jobs: - name: Create PR Preview Comment if: github.event_name == 'pull_request' && steps.deploy-preview-comment.outputs.comment-id == 0 - uses: peter-evans/create-or-update-comment@v1.4.5 + uses: peter-evans/create-or-update-comment@v5 with: issue-number: ${{ github.event.pull_request.number }} body: | @@ -192,7 +297,7 @@ jobs: - name: Update PR Preview Comment if: github.event_name == 'pull_request' && steps.deploy-preview-comment.outputs.comment-id != 0 - uses: peter-evans/create-or-update-comment@v1.4.5 + uses: peter-evans/create-or-update-comment@v5 with: comment-id: ${{ steps.deploy-preview-comment.outputs.comment-id }} edit-mode: replace diff --git a/.github/workflows/render-datasheets.yaml b/.github/workflows/render-datasheets.yaml index 2a26f81258..76951a6d20 100644 --- a/.github/workflows/render-datasheets.yaml +++ b/.github/workflows/render-datasheets.yaml @@ -10,10 +10,10 @@ jobs: REPO_ACCESS_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }} steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: - node-version: 18 + node-version: 22 cache: "npm" cache-dependency-path: "**/package-lock.json" diff --git a/.github/workflows/stale-branch-cleanup.yml b/.github/workflows/stale-branch-cleanup.yml new file mode 100644 index 0000000000..e44655f333 --- /dev/null +++ b/.github/workflows/stale-branch-cleanup.yml @@ -0,0 +1,271 @@ +name: Stale Branch Cleanup + +on: + schedule: + # Runs at 03:00 UTC every Monday + - cron: '0 3 * * 1' + workflow_dispatch: + inputs: + dry_run: + description: 'Only report what would happen, without commenting, closing or deleting.' + required: false + default: true + type: boolean + days_threshold: + description: 'Treat a branch as stale when it has had no commits and no pushes for this many days.' + required: false + default: '365' + type: string + +permissions: + contents: write + pull-requests: write + issues: write + +jobs: + cleanup: + name: Remind on stale pull requests, delete abandoned branches + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Process stale branches + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + # A cron has nobody watching it, so it only reports unless a person dispatches and asks for the real run + DRY_RUN: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.dry_run }} + # For scheduled runs the inputs are empty, so fall back to defaults. + DAYS_THRESHOLD: ${{ github.event.inputs.days_threshold || '365' }} + # A stale pull request gets this many reminders, one per run, before it is closed. + MAX_REMINDERS: '3' + REMINDER_INTERVAL_DAYS: '7' + # Branches and pull requests carrying this label are never touched. + EXEMPT_LABEL: 'keep-open' + run: | + set -euo pipefail + + THRESHOLD_SECONDS=$(( DAYS_THRESHOLD * 24 * 60 * 60 )) + # Half a day of slack so a weekly cron is never skipped for firing a few minutes early. + REMINDER_SECONDS=$(( REMINDER_INTERVAL_DAYS * 24 * 60 * 60 - 43200 )) + NOW=$(date +%s) + + REMINDER_MARKER='' + CLOSED_MARKER='' + + echo "Repository: $REPO" + echo "Default branch: $DEFAULT_BRANCH" + echo "Stale after: $DAYS_THRESHOLD days with no commits and no pushes" + echo "Reminders: $MAX_REMINDERS, one every $REMINDER_INTERVAL_DAYS days" + echo "Dry run: $DRY_RUN" + echo "----------------------------------------" + + # Open pull requests whose head branch lives in this repository. Pull requests from + # forks are out of scope: their branch is not ours to delete. + gh pr list --repo "$REPO" --state open --limit 500 \ + --json number,headRefName,author,isCrossRepository,labels,createdAt \ + --jq '.[] | select(.isCrossRepository == false) + | [.headRefName, .number, .author.login, .createdAt[0:10], + ([.labels[].name] | join(","))] | @tsv' > /tmp/open_prs.tsv + + # Deleting the base branch of an open pull request closes it, so those are protected + # too, even when the base branch itself is old. Fork pull requests count here. + gh pr list --repo "$REPO" --state open --limit 500 \ + --json baseRefName --jq '.[].baseRefName' | sort -u > /tmp/base_branches.txt + + # A branch can carry old commits and still be new: it may have been cut from an old + # commit, or had old history force-pushed onto it yesterday. The tip commit date on + # its own would call that stale, so pair it with the repository activity log, which + # records when each ref was really created or pushed to. If this call fails the job + # fails with it, on purpose: better a red run than deleting branches unguarded. + ACTIVITY_QUERY='per_page=100' + if [ "$DAYS_THRESHOLD" -le 365 ]; then + ACTIVITY_QUERY="$ACTIVITY_QUERY&time_period=year" + fi + gh api "repos/$REPO/activity?$ACTIVITY_QUERY" --paginate \ + --jq '.[] | select(.activity_type == "push" or .activity_type == "force_push" + or .activity_type == "branch_creation") + | select(.ref | startswith("refs/heads/")) + | [(.timestamp | fromdateiso8601), (.ref | sub("^refs/heads/"; ""))] | @tsv' \ + | awk -F'\t' '$1 > seen[$2] { seen[$2] = $1 } + END { for (b in seen) print seen[b] "\t" b }' > /tmp/branch_activity.tsv + + echo "Open pull requests from this repository: $(wc -l < /tmp/open_prs.tsv)" + echo "Branches used as a base by an open pull request: $(wc -l < /tmp/base_branches.txt)" + echo "Branches created or pushed to within the activity window: $(wc -l < /tmp/branch_activity.tsv)" + echo "----------------------------------------" + + PROTECTED_REGEX="^(${DEFAULT_BRANCH}|main|master|develop|staging|production|gh-pages)$" + + reminded=0 + closed=0 + deleted=0 + waiting=0 + skipped=0 + + while IFS=$'\t' read -r commit_ts branch_ref; do + branch="${branch_ref#origin/}" + + # Skip the HEAD pointer entry and empty lines. + if [ -z "$branch" ] || [ "$branch" = "HEAD" ]; then + continue + fi + + if echo "$branch" | grep -Eq "$PROTECTED_REGEX"; then + skipped=$((skipped + 1)) + continue + fi + + if [ $(( NOW - commit_ts )) -lt "$THRESHOLD_SECONDS" ]; then + skipped=$((skipped + 1)) + continue + fi + + # Old commits are not enough on their own: the ref must also have been left alone. + activity_ts=$(awk -F'\t' -v b="$branch" '$2 == b { print $1; exit }' /tmp/branch_activity.tsv) + if [ -n "$activity_ts" ] && [ $(( NOW - activity_ts )) -lt "$THRESHOLD_SECONDS" ]; then + echo "SKIP (old commits, but created or pushed on $(date -u -d "@$activity_ts" '+%Y-%m-%d')): $branch" + skipped=$((skipped + 1)) + continue + fi + + if grep -qxF "$branch" /tmp/base_branches.txt; then + echo "SKIP (base branch of an open pull request): $branch" + skipped=$((skipped + 1)) + continue + fi + + last_commit_date=$(date -u -d "@$commit_ts" '+%Y-%m-%d') + stale_days=$(( (NOW - commit_ts) / 86400 )) + + pr_row=$(awk -F'\t' -v b="$branch" '$1 == b { print; exit }' /tmp/open_prs.tsv) + + # No open pull request: nothing to discuss, the branch just goes. + if [ -z "$pr_row" ]; then + if [ "$DRY_RUN" = "true" ]; then + echo "WOULD DELETE branch (no open pull request): $branch (last commit: $last_commit_date)" + else + echo "DELETING branch (no open pull request): $branch (last commit: $last_commit_date)" + if ! git push origin --delete "$branch"; then + echo " WARNING: could not delete $branch, leaving it in place" + continue + fi + fi + deleted=$((deleted + 1)) + continue + fi + + IFS=$'\t' read -r _ pr_number pr_author pr_created pr_labels <<< "$pr_row" + + if echo ",$pr_labels," | grep -qF ",$EXEMPT_LABEL,"; then + echo "SKIP (labelled $EXEMPT_LABEL): PR #$pr_number <- $branch" + skipped=$((skipped + 1)) + continue + fi + + # Count the reminders we already posted. Only those newer than the last commit + # count, so pushing to the branch restarts the cycle from scratch. + reminder_count=0 + last_reminder_ts=0 + while read -r created_at; do + if [ -z "$created_at" ]; then + continue + fi + reminder_ts=$(date -u -d "$created_at" +%s) + if [ "$reminder_ts" -gt "$commit_ts" ]; then + reminder_count=$((reminder_count + 1)) + if [ "$reminder_ts" -gt "$last_reminder_ts" ]; then + last_reminder_ts=$reminder_ts + fi + fi + done < <(gh api --paginate "repos/$REPO/issues/$pr_number/comments" \ + --jq ".[] | select(.body | contains(\"$REMINDER_MARKER\")) | .created_at" \ + < /dev/null || true) + + # Nothing is due yet if the previous reminder is younger than the interval. + if [ "$reminder_count" -gt 0 ] && \ + [ $(( NOW - last_reminder_ts )) -lt "$REMINDER_SECONDS" ]; then + echo "WAIT (reminder $reminder_count/$MAX_REMINDERS sent $(date -u -d "@$last_reminder_ts" '+%Y-%m-%d')): PR #$pr_number <- $branch" + waiting=$((waiting + 1)) + continue + fi + + if [ "$reminder_count" -lt "$MAX_REMINDERS" ]; then + next=$((reminder_count + 1)) + deletion_date=$(date -u -d "+$(( (MAX_REMINDERS - next + 1) * REMINDER_INTERVAL_DAYS )) days" '+%Y-%m-%d') + + body=$(cat <> "$GITHUB_STEP_SUMMARY" + + echo "Reminders sent: $reminded | Pull requests closed: $closed | Branches deleted: $deleted | Waiting: $waiting | Untouched: $skipped" diff --git a/.github/workflows/sync-private-mirror.yml b/.github/workflows/sync-private-mirror.yml index db17e618a2..a8c1715232 100644 --- a/.github/workflows/sync-private-mirror.yml +++ b/.github/workflows/sync-private-mirror.yml @@ -8,7 +8,7 @@ jobs: if: "github.repository == 'arduino/docs-content'" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 with: fetch-depth: "0" ref: "main" diff --git a/.github/workflows/sync-pull-request.yml b/.github/workflows/sync-pull-request.yml index 8fa8e66f6d..8efd48b012 100644 --- a/.github/workflows/sync-pull-request.yml +++ b/.github/workflows/sync-pull-request.yml @@ -7,7 +7,7 @@ jobs: if: "github.repository == 'arduino/docs-content-private'" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 with: fetch-depth: "0" diff --git a/.github/workflows/workflow-validate.yaml b/.github/workflows/workflow-validate.yaml index e72ff090f5..6f78d49ddb 100644 --- a/.github/workflows/workflow-validate.yaml +++ b/.github/workflows/workflow-validate.yaml @@ -14,7 +14,7 @@ jobs: name: Spell Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Run spell check run: | pip install codespell==2.3.0 @@ -30,7 +30,7 @@ jobs: node_version: [14] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Run linter run: | diff --git a/content/hardware/02.uno/boards/uno-q/datasheet/it-datasheet.md b/content/hardware/02.uno/boards/uno-q/datasheet/it-datasheet.md index 28708c68c7..72c164819f 100644 --- a/content/hardware/02.uno/boards/uno-q/datasheet/it-datasheet.md +++ b/content/hardware/02.uno/boards/uno-q/datasheet/it-datasheet.md @@ -997,9 +997,8 @@ Le guide d’utilisation des dispositifs pour réseaux locaux doit inclure des i (i) les dispositifs fonctionnant dans la bande 5 150-5 250 MHz sont réservés uniquement pour une utilisation à l’intérieur afin de réduire les risques de brouillage préjudiciable aux systèmes de satellites mobiles utilisant les mêmes canaux ; -
- Note: For 5GHz and/or when co-located with 5 GHz transmitters, the following statements should be provided in the user information. -
+> [!NOTE] +> **Note:** For 5GHz and/or when co-located with 5 GHz transmitters, the following statements should be provided in the user information. **Radiation Exposure Statement** diff --git a/package-lock.json b/package-lock.json index aebbf45da1..74b60011e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "hasInstallScript": true, "license": "ISC", "dependencies": { - "@arduino/docs-arduino-cc": "^2.2.81", + "@arduino/docs-arduino-cc": "2.2.9", "gatsby": "^5.11.0", "gatsby-background-image": "^1.6.0", "gatsby-image": "^3.11.0", @@ -315,9 +315,9 @@ } }, "node_modules/@arduino/docs-arduino-cc": { - "version": "2.2.81", - "resolved": "https://npm.pkg.github.com/download/@arduino/docs-arduino-cc/2.2.81/1300e5fda82cbd34ea78198c5984571d1d76e2b5", - "integrity": "sha512-wr3OvvGfzqR/xxGPZfxzHG2xDzrAXwh1D69h/W1alnIKOmG2ujs8orb3EntfKIOJmKfxixZqepBqYt0hckOaYQ==", + "version": "2.2.9", + "resolved": "https://npm.pkg.github.com/download/@arduino/docs-arduino-cc/2.2.9/f8e62faa785b887e959c1e8108e9cfe82d0cc5f4", + "integrity": "sha512-yk22JYPzLiQIwX3oCY02GAWw+wCPL4lCg3F0XQLw4tPBYvMyKGaU0+rAXQ9lmZfZfMrTXveXuZsaLX5yXBwx6g==", "dependencies": { "@algolia/autocomplete-core": "^1.10.0", "@algolia/autocomplete-plugin-recent-searches": "^1.17.0", diff --git a/package.json b/package.json index d51b841f84..42cdd59510 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ }, "homepage": "https://github.com/arduino/docs-content#readme", "dependencies": { - "@arduino/docs-arduino-cc": "^2.2.81", + "@arduino/docs-arduino-cc": "2.2.9", "gatsby": "^5.11.0", "gatsby-background-image": "^1.6.0", "gatsby-image": "^3.11.0", @@ -42,8 +42,8 @@ "npm": "^10.4.0" }, "volta": { - "node": "18.16.1", - "npm": "9.8.0" + "node": "22.22.3", + "npm": "10.9.8" }, "devDependencies": { "fast-glob": "^3.3.3", diff --git a/scripts/datasheet-rendering/package-lock.json b/scripts/datasheet-rendering/package-lock.json index 0da7511b4f..16947aeeb4 100644 --- a/scripts/datasheet-rendering/package-lock.json +++ b/scripts/datasheet-rendering/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "datasheet-renderer": "git+https://github.com/arduino/datasheet-renderer.git#e36fd5549e1a351c0d85843109817653c8dce15b" + "datasheet-renderer": "git+https://github.com/arduino/datasheet-renderer.git#97e240e01be744e5e34ea13613f0c69c48100cd9" } }, "node_modules/@babel/code-frame": { @@ -443,9 +443,9 @@ } }, "node_modules/datasheet-renderer": { - "version": "1.0.6", - "resolved": "git+ssh://git@github.com/arduino/datasheet-renderer.git#e36fd5549e1a351c0d85843109817653c8dce15b", - "integrity": "sha512-7SLMplDVUb/KfP46U9CiPMShRQ/gkKWfknLJ8h4DANOV2T3jBQfVLq/4JMGTxnRB1zVk2o1iyEHT/lLaLnMzVQ==", + "version": "1.0.7", + "resolved": "git+ssh://git@github.com/arduino/datasheet-renderer.git#97e240e01be744e5e34ea13613f0c69c48100cd9", + "integrity": "sha512-mvjfLs19L6LHM/+Ll6JbO+QChfbj3IRv1rq0ObgaEGC5rdghwhuFwAfNDjWF+tyqIhZHp8mChZo6GXgZNsGbpQ==", "license": "ISC", "dependencies": { "express": "^4.18.2", diff --git a/scripts/datasheet-rendering/package.json b/scripts/datasheet-rendering/package.json index 2df449b7ce..94a448fd06 100644 --- a/scripts/datasheet-rendering/package.json +++ b/scripts/datasheet-rendering/package.json @@ -10,6 +10,6 @@ "author": "Sebastian Romero", "license": "ISC", "dependencies": { - "datasheet-renderer": "git+https://github.com/arduino/datasheet-renderer.git#e36fd5549e1a351c0d85843109817653c8dce15b" + "datasheet-renderer": "git+https://github.com/arduino/datasheet-renderer.git#97e240e01be744e5e34ea13613f0c69c48100cd9" } } diff --git a/scripts/datasheet-rendering/styles/common-style.css b/scripts/datasheet-rendering/styles/common-style.css index 84e8db2eca..7acdb35d24 100644 --- a/scripts/datasheet-rendering/styles/common-style.css +++ b/scripts/datasheet-rendering/styles/common-style.css @@ -296,4 +296,31 @@ h1::after, h2::after, h3::after, h4::after { display: block; height: 300px; margin-bottom: -300px; -} \ No newline at end of file +} + +/* Notes written as plain markdown blockquotes, so a datasheet needs no inline HTML for them */ +blockquote { + background-color: rgba(0, 170, 228, 0.2); + border-left: 6px solid rgba(0, 120, 180, 1); + margin: 20px 0; + padding: 15px; +} + +blockquote > :first-child { + margin-block-start: 0; +} + +blockquote > :last-child { + margin-block-end: 0; +} + +/* Severities from a [!WARNING] or [!CAUTION] marker, same colours the inline HTML notes used */ +blockquote.warning { + background-color: #FFFFE0; + border-left-color: #FFD700; +} + +blockquote.caution { + background-color: #FFCCCC; + border-left-color: #FF0000; +}