The manifest is no longer considered for skipping test since
https://github.com/vercel/next.js/pull/81170. It also didn't update
anymore automatically so I removed updating areweturboyet.com which was
broken for a while now anyway when we switched away from static tokens.
Became orphaned in https://github.com/vercel/next.js/pull/97792. Another
script and the test harness needed some utils from `next-stats-action`
so I moved that into `test/lib/link-packed-packages`.
The `popular` workflow posted weekly Slack digests of the most-reacted
issues, PRs, and feature requests. That reporting is now handled by the
Next.js maintainer agent, so this PR deletes the workflow along with its
backing `next-repo-actions` action (sources and checked-in ncc bundles),
which had no other consumer, and regenerates the `.github` lockfile
without the action's dependencies.
Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
The `issue_wrong_template.yml` workflow closed issues labeled `please
use the correct issue template`. That label was only ever applied
manually by maintainers and has not been used since January 2025 (9
issues total, ever), so every recent run of the workflow was skipped.
This change removes the workflow along with its backing
`wrong-issue-template` action (source, checked-in ncc bundle, and build
script).
Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
CI authenticated to Vercel Remote Cache with a long-lived Personal
Access Token in the `TURBO_TOKEN` repository secret. That token never
expired, is scoped to a team member rather than the team, and is
readable by every job that inherits secrets.
Each job now mints its own short-lived, cache-only token instead, using
`vercel/setup-turborepo-remote-cache-action` against a Turborepo CLI
OIDC policy configured on the Vercel team following
https://vercel.com/docs/monorepos/remote-caching/external-ci-cd#openid-connect-oidc
Forks skip the step entirely since they won't have access to repository
variables. During outages or any other permission errors, the steps
outcome will simply be ignore and we fall back to uncached behavior.
This could lead to silent regressions or hiding new, incorrect callsites
lacking necessary permissions. A Datadog monitor is not as simple as I'd
like since DD does not track outcome but conclusion (which is always
success for continue-on-error). Adding custom tags via DD CLI feels to
heavy. We'll revisit if this becomes a recurring issue.
## Summary
- Let issue authors and people who commented before closure reopen an
issue within 14 days using `Reopen: <reason>`.
- Limit the automation to unlocked issues closed by triage-or-higher
human maintainers, with ordered timeline checks, bot filtering,
idempotency, and state revalidation.
- Align resolved-issue and contributor guidance with the new reopening
window.
## Verification
- `Prettier 3.6.2 --check .github/workflows/issue_reopen.yml
.github/comments/resolved.md contributing/repository/triaging.md`
- Mocked workflow harness covering 19 closure, eligibility, boundary,
bot, idempotency, and race scenarios
- `autoreview --mode local --stream-engine-output` (Codex and Claude
clean)
- Live GitHub API spot-check for ordered same-second `commented` and
`closed` timeline events
- Not run: live issue close/reopen smoke test (issue-triggered workflows
run only from the default branch)
This is a pretty minor win, but it can save a few seconds per job.
https://github.blog/changelog/2026-06-25-actions-steps-can-now-be-run-in-parallel/
Also remove the installation of `lld` for rustc with `apt-get`. It's
very slow, and rustc should already ship with `lld` these days? The
composite action to install the rust toolchain isn't run often (test
shards download a prebuilt native binary), but `build-native` it can
block other jobs from running and become a bottleneck.
Testing the full `build-and-deploy` job, which uses the rust install
action: https://github.com/vercel/next.js/actions/runs/30138483769
The `Stats (webpack)` / `Stats (turbopack)` CI jobs have been
intermittently failing on PRs and canary (e.g. [this
run](https://github.com/vercel/next.js/actions/runs/29476089144/job/87563318723)).
The jobs run on arm64 runners, where the `ubuntu:24.04` base image
fetches apt packages from `ports.ubuntu.com`, which is intermittently
unreachable. Since `apt-get update` only warns when index fetches fail,
the Docker build breaks one layer later at `apt-get install` with
"Unable to locate package curl".
This change switches the base image to `buildpack-deps:noble-scm`, which
already ships curl, ca-certificates, and git, so the build no longer
makes any apt calls at all. The `apt-get upgrade` step is dropped along
with it; it was already a silent no-op whenever the mirror was down, and
the ephemeral CI container does not rely on it.
Both `.github/next-stats-action.Dockerfile` (the one CI builds via
`action.yml`) and the duplicate
`.github/actions/next-stats-action/Dockerfile` are updated to stay in
sync.
We used to not post a comment when a part of the bundler matrix got
cancelled. Conceptually, a cancelled job is just like a failed on in the
sense of not being able to produce stats.
Now we always aggregate stats and post stats from the bundlers that did
produce them.
The risk here is a race between a run that was cancelled because another
run superseded it and the run that superseded it. Though practically,
aggregating the stats isn't slower than running the full workflow. Not
impossible (especially given how slow the Webpack job is) but incredibly
rare.
A 16 core x86-64 runner is $0.042/minute. A 16 core arm64 runner is
$0.026/minute.
https://docs.github.com/en/billing/reference/actions-runner-pricing
It also looks like they're faster! After factoring the performance
improvement, this appears to be a ~50% cost reduction for migrated jobs.
Datadog SQL:
```
SELECT
REGEXP_REPLACE(ci_job_name, '\s*\([0-9]+/[0-9]+\)', '') AS ci_job_name,
labels,
branch,
commit_date,
SUM(duration) / 1000 / 1000 / 1000 / 60 as minutes,
ci_pipeline_id
FROM dd.ci_pipelines(
columns => ARRAY ['@git.commit.committer.date', '@ci.pipeline.id', '@duration', '@git.branch', '@ci.node.labels', '@ci.job.name'],
event_type => 'job',
filter => '@git.repository.id_v2:github.com/vercel/next.js @ci.pipeline.name:build-and-test @ci.provider.instance:github-actions @ci.pipeline.id:(28266192824-1 OR 28266190122-2) -@ci.job.name:*changed*',
from_timestamp => now() - INTERVAL '30 days',
to_timestamp => now()
) AS (
commit_date VARCHAR,
ci_pipeline_id VARCHAR,
duration BIGINT,
branch VARCHAR,
labels VARCHAR,
ci_job_name VARCHAR
)
GROUP BY
REGEXP_REPLACE(ci_job_name, '\s*\([0-9]+/[0-9]+\)', ''),
labels,
commit_date,
branch,
ci_pipeline_id
ORDER BY
REGEXP_REPLACE(ci_job_name, '\s*\([0-9]+/[0-9]+\)', ''),
branch,
commit_date,
minutes;
```
https://app.datadoghq.com/ddsql/editor?ddsql_dataset_id=34de66b9-30f4-48dd-ac98-354dc7e67fdf
Full Data:
https://docs.google.com/spreadsheets/d/1SszR3RsPeJ13nVW5ggMakwy87fkQMqWMHbZDehHH8IA/edit?usp=sharing
Here's the abbreviated data for jobs longer than 5 minutes:
<img width="1187" height="656" alt="Screenshot 2026-06-26 at 4 38 00 PM"
src="https://github.com/user-attachments/assets/9341039e-c4ea-405c-8d24-cdbc5ed1ba24"
/>
It seems like for the jobs switched over to arm64, they run in about
~73-82% of the time as arm.
## Summary
Switch CI from self-hosted runners to GitHub-hosted runners and fix the
follow-up issues that showed up once the hosted jobs were exercised.
## Changes
- Move Linux workflows onto `ubuntu-latest-16-core-oss`, Windows
workflows onto `windows-latest-8-core-oss`, and the native mac release
lane onto `macos-15-intel`.
- Update the reusable build workflow to work in hosted environments by
relying on `runner.os` instead of label string matching, and by removing
the hardcoded `/home/runner` `fnm` path.
- Make Turbo/sccache configuration explicit for hosted runners:
- unify on `vtest314-next-adapter-e2e-tests`
- pass `TURBO_TOKEN` via workflow env/secrets instead of assuming
runner-level env
- switch Turbo cache mode to `local:rw,remote:rw` so jobs still have
local cache behavior when remote cache is unavailable
- Update the `sccache` action defaults/docs to use the hosted-runner
secret setup (`TURBO_TOKEN`) instead of the old self-hosted token
wiring.
- Make Datadog reporting fail open so missing `DATA_DOG_API_KEY` does
not block CI.
- Fix the CLI reserved-port test so it asserts Next’s own `-p 1`
validation instead of failing earlier on hosted Linux due to
privileged-port binding.
- Replace the live `musl.cc` dependency in `native-builder.Dockerfile`
with musl sysroots imported from GHCR-hosted `rust-musl-cross` images,
keeping the existing `/opt/*-cross` layout used by the Linux native
build scripts.
- Fix the macOS native build workaround s so it applies to all
*-apple-darwin targets based on the Cargo target OS rather than the host
architecture, which avoids napi-build injecting the unsupported -Wl
linker arg when @next/swc is linked with rust-lld on hosted mac runners.
Test Plan:
- This PR's CI
- This
[build_and_deploy](https://github.com/vercel/next.js/actions/runs/24865715983/job/72801225722)
job running on every arch
## Summary
PR #90709 introduced a `githubHeadSha` field in the stats action using
`event.after || GITHUB_SHA`, which can resolve to the wrong SHA for PR
events. The correct value was already available as `commitId` (from
`getCommitId()` = `git rev-parse HEAD` on the cloned PR branch).
- Use `commitId` for the tarball URL in `add-comment.js`
- Remove the redundant `githubHeadSha` field from `action-info.js`
## Test plan
- [ ] Verify the stats comment on a PR shows the correct tarball URL
matching the deployed artifact
### What?
Adds a new "Native Binary" section to the `Stats from current PR`
comment posted by the `generate-pull-request-stats` GitHub Action. The
section reports the size of the SWC native binary
(`packages/next-swc/native/*.node`) built for the PR and the change
relative to the latest value recorded on `canary`.
Example row:
> | Metric | Canary | PR | Change | Trend |
> |:-------|-------:|---:|-------:|:-----:|
> | SWC Binary Size | 113 MB | 121 MB | 🔴 +7.34 MB (+6%) | ▁▃▅▇█ |
### Why?
The `.node` binary is a sizable, user-facing artifact (it ships with
`next`). Today there's no visibility on its size in PRs, so regressions
from Rust/Cargo changes are easy to miss. Surfacing the size (and a diff
vs. canary) in the existing stats comment gives immediate feedback on
any change that affects the binary.
### How?
- **Measurement
(`.github/actions/next-stats-action/src/run/index.js`):** After the
action copies the pre-built native binary into the working dirs, sum the
size of every `*.node` file under the action's `native/` directory and
store it as `General.swcBinarySize` on both the canary and PR stat sets.
- **Canary baseline via KV history
(`.github/actions/next-stats-action/src/add-comment.js`):** The workflow
downloads a single pre-built binary and copies it into both the canary
and PR checkouts, so an in-run diff would always be zero. Instead, for
PR runs we override `mainRepoStats.General.swcBinarySize` with the most
recent `swcBinarySize` from the Vercel KV history (the same store
already used for other canary metrics). Canary runs keep the measured
value, and the existing persistence path (`saveToHistory` / `General`)
writes it back so future PRs see an updated baseline. If no history
exists yet, the Canary column renders `N/A` and no change is shown.
- **Rendering:** New `generateNativeBinarySection` emits a
`<details>`-wrapped section styled like Bundle Sizes / All Metrics,
rendered right after the Bundle Sizes block. It reuses `prettify`,
`formatChange`, and `generateTrendBar` so formatting, thresholds, and
sparklines are consistent with the rest of the comment. A tight
threshold entry (`swcBinarySize: { absoluteMin: 10 KB, percentMin: 0.5%,
percentOnly: 0.05% }`) is added so meaningful movement is flagged while
tiny determinism noise is ignored.
- **Top-line summary:** Because `swcBinarySize` lives on `General`, the
existing `generateChangeSummary` automatically promotes significant
changes into the headline regression/improvement table at the top of the
comment, using the label `SWC Binary Size`.
- **Sharded runs:** The workflow runs two sharded jobs (Webpack and
Turbopack). The binary is bundler-independent, so both shards report the
same value; `aggregate-results.js` merges `General` via `Object.assign`,
which is correct here (last-writer-wins on an identical value).
No workflow changes are required — the existing `pull_request_stats.yml`
already downloads the `next-swc-binary` artifact and copies it into the
action's `native/` directory.
### Verification
Smoke-tested `add-comment.js` locally with `LOCAL_STATS=1` in two
scenarios:
- No KV history configured → Canary column renders `N/A`, PR value
renders, Change is `-`, section still shown.
- Mocked KV history containing prior `swcBinarySize` values → full diff
rendered in both the Native Binary section and the top-level
significance summary, with a sparkline of the last 5 historical entries.
Closes NEXT-
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
## What
- Use `cargo binstall` instead of `cargo install` for Rust tools in the
Docker image
- Download pre-built vercel/sccache from GitHub releases instead of
compiling from git
## Why
`cargo install` compiles from source which is slow on cache miss. `cargo
binstall` downloads pre-built binaries in seconds. The vercel/sccache
fork now publishes release binaries, eliminating the need for the
`build-sccache` turbo task (~2-3 min compile).
## Changes
**Dockerfile:**
- Install cargo-binstall at pinned version (1.18.1), use it for
cargo-rustflags
- Remove sccache from Docker image (not needed inside container)
**CI sccache:**
- Download pre-built binary from `vercel/sccache` GitHub releases
(~11MB)
- Cache in `~/.cache/sccache-<version>` on self-hosted runners
- Delete `scripts/build-sccache.js`, remove turbo task and package.json
entry
- `scripts/sccache-version` now contains just the release tag
<!-- NEXT_JS_LLM_PR -->
### What?
Eliminates the expensive temporary repo directory (`tmpRepoDir`) copy
during isolated test setup by leveraging a Turborepo `pack` task with
caching.
### Why?
The previous test isolation flow copied the entire `packages/` directory
to a temp location, mutated every `package.json` to rewrite workspace
dependency references, and ran `pnpm pack` sequentially for each
package. This added ~10s+ of overhead per test suite. With Turborepo
caching, repeated runs are 500ms (and can still be improved).
### How?
- Adds a `pack-for-isolated-tests` task to `turbo.json` (depends on
`build`, outputs `packed.tgz`)
- Adds a `pack-for-isolated-tests` script (`pnpm pack --out
./packed.tgz`) to every workspace package
- Simplifies `linkPackages` in `repo-setup.js` to scan for pre-built
tarballs instead of copying/rewriting/packing
- Updates `create-next-install.js` to run `pnpm turbo run pack`
(benefits from caching) and use package manager `overrides`
(pnpm/npm/yarn) to resolve transitive workspace deps from local tarballs
- Removes `tmpRepoDir` handling from `base.ts` and `run-tests.js`
**Before:** `createNextInstall` ~16s (copy + sequential pack)
**After:** `createNextInstall` ~5s (first run) / ~4s (cached)
<!-- NEXT_JS_LLM_PR -->
`pnpm dlx` isn't using a lockfile. Using the self-contained binary from
GitHub and validate the checksum lets us effectively pin `datadog-ci`
and all of its possible transitive dependencies.
This is a follow-up for
https://vercel.slack.com/archives/C0APPN2LC83/p1775073127894859
## What
Use sccache, backed by the Vercel artifact cache, when compiling Rust
code.
## Why
Each CI runner compiles ~960 external Rust crates from scratch (~5 min)
on binary cache misses, even though `Cargo.lock` rarely changes between
commits. Some workflows don't cache and always build from scratch.
## Notes
Builds will use a local cache on each runner, but we've got a lot of
these runners and they'll take some time to populate that local, fastest
level of cache.
Windows builds are currently hitting ~68% of cached compilations for a
reason that isn't totally clear. Linux builds are caching correctly,
however. A follow-up pass at this work will use an incremental turborepo
caching strategy to improve this, and another pass will experiment with
cross-compiling Windows from Linux which will likely also help.
Linux builds that don't change Rust files at all are nearly
instantaneous (as before), and Linux builds that change Rust files will
complete in approximately 1/2 the time. `check` builds are ~1min faster
(~25% faster).
LTO-heavy runs like the benchmark builders are currently still slow
(approx unchanged), needs further investigation (may be related to git
hash embedding?).
This job has been failing on canary pushes, but not on PRs. I'm not sure why it's only happening in canary pushes, but hopefully this should fix the specific error we're seeing when it does fail.
Discussion: https://vercel.slack.com/archives/C04KC8A53T7/p1775571376147179
Previously, we were just copying the `package.json` into the container and `pnpm install`ing it.
This wouldn't respect the lockfile or `minimumReleaseAge`. This uses `pnpm deploy` based on the documentation here: https://pnpm.io/docker
This should fix the stats action: We were checking out Next.js 16.2.2, and that doesn't support the `--out` flag for `pnpm pack`. Let's just force pnpm 10.33.0 here for now.
While we're here, I made some other updates and improvements:
- Update dockerfile to use Ubuntu 24.04
- Update dockerfile to use corepack 0.34.6
- Fix an issue where `pnpm install`ing outside of the docker container could break the docker container build (use `--exclude=node_modules`)
- Avoid using a shell with string interpolation (potential escaping issues) for `cd`ing, just set `cwd`.
We recently re-imaged the self-hosted Linux runners and it now hits
`ERR_PNPM_EXDEV` when pnpm copies packages between its default store and
the temp stats workspace. Keeping both under the same temp root avoids
the cross-filesystem copy failure.
The previous method of using the PR endpoint under the hood calls the
GitHub API to get the commit SHA, which is a waste of resources, so just
use the commit SHA directly in the tarball URL.
## Summary
- fix flaky `pnpm install` failures in the stats action caused by
cross-device hardlinking (`ERR_PNPM_EXDEV`)
- force `pnpm` to use `--package-import-method=copy` for installs run in
temp repos
- apply the same setting in both install paths used by the stats
workflow
x-ref:
https://github.com/vercel/next.js/actions/runs/22110883672/job/63906643545
## What?
Measures the time to `Ready in X ms` in the PR stats action so that
improvements/regressions on it are highlighted on each PR.
---------
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
## Summary
Add per-metric significance thresholds that account for variance.
**Time metrics** (high variance from CI):
- Dev boot: <100ms AND <15%, OR <3%
- Build times: <500ms AND <5%, OR <2%
**Size metrics** (deterministic):
- node_modules: <10KB AND <1%, OR <0.01%
- Bundle sizes: <2KB AND <1%, OR <0.1%
## Problem
Current thresholds flag insignificant changes as regressions:
- 3.45 KB change on 457 MB node_modules (0.0008%) was flagged as a
regression
## Solution
Use per-metric thresholds with OR logic for very small percentage
changes:
- Time metrics need more generous thresholds due to CI variance
- Size metrics can be tighter since they're deterministic
## Test plan
- Verify that 3.45 KB on 457 MB (0%) is now marked as insignificant
- Verify that real regressions (>0.1% for sizes, >3% for times) are
still flagged
---
Stack:
- #88157
- **→ This PR**
## Summary
Comprehensive improvements to the PR stats action for more reliable benchmarks and reduced CI noise.
### Vercel KV Integration
- Add `@vercel/kv` for historical data persistence
- Track metrics over time with `loadHistory()` / `saveToHistory()`
- Display trend sparklines in comments when history is available
### Comment Generation Refactor
- New `METRIC_LABELS` and `METRIC_GROUPS` configuration system
- Organized metrics by category (Dev Server, Production Builds, Production Runtime)
- Better formatting utilities (`prettifyTime`, `formatChange`, `generateTrendBar`)
- Bundle group totals for KV persistence
### Noise Reduction
- Significance thresholds: `50ms AND 10%` for time, `1KB AND 1%` for size
- Additional `<2%` filter for long-running operations (builds)
- Filters typical CI variance while catching real regressions
### Stats Config Updates
- Add `measureDevBoot: true` to enable dev boot benchmarks
- Fix `appDevCommand` to include `dev` subcommand
- Add `--webpack` flag to `appBuildCommand`
- Rename "Client Bundles (main, webpack)" to "Client Bundles (main)"
- Add `turbopack: {}` to test configs
### GitHub Actions Updates
- New `action.yml` with bundler input parameter
- Workflow improvements in `pull_request_stats.yml`
### New Files
- `scripts/test-stats-benchmark.sh` - Local testing script
- `.github/actions/next-stats-action/src/util/stats.js` - Shared stats utilities
- `.github/actions/next-stats-action/test-local.js` - Local comment testing
## Test Plan
```bash
cd .github/actions/next-stats-action
node test-local.js # Basic test
node test-local.js --with-history # Test with trend sparklines
```
Update zod v3 to latest. Noticed this while playing with ai sdk as it's requires new versions of zod. Hence realized we haven't updated for a while. Bump to newer versions of `zod@3`
Cleans up unused workflows & scripts
- bankrupt workflow is superseded by the [stale
workflow](https://github.com/vercel/next.js/blob/canary/.github/workflows/issue_stale.yml)
- filtering issues by version hasn't been used in 9 months and can be
accomplished with a GH search
- the AI triaging tool has largely been more noise than signal. the
existing alerts that have been surfaced in the triage channel have been
sufficient
- Add the domain to the `upload-turboyet-data` action so that cache invalidation can happen faster (instead of waiting 15 minutes for the TTL).
- No longer worry about putting "rspack" in PR titles.
🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀
