Skip to content

Replace the deprecated localstack CLI with lstk in Samples CI and the sample guides - #135

Merged
paolosalvatori merged 6 commits into
mainfrom
feature/migrate-samples-ci-to-lstk
Oct 13, 2026
Merged

paolosalvatori merged 6 commits into
mainfrom
feature/migrate-samples-ci-to-lstk

Conversation

@paolosalvatori

@paolosalvatori paolosalvatori commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

Samples CI starts the emulator, waits for it and reads its logs with the localstack CLI, which is deprecated and no longer receives updates: every job prints WARNING: The 'localstack' CLI is deprecated. Use the 'lstk' CLI instead. The workflow already installs lstk, but only for lstk az start-interception. The sample READMEs and docs/LOCALSTACK.md start the emulator with the same CLI (IMAGE_NAME=localstack/localstack-azure localstack start -d, then localstack wait), while 45 of them list only lstk among their prerequisites, so a reader who follows them has no localstack command.

The last run on main (38376597563) shows three more problems, which this PR also fixes:

  • False readiness timeouts. Five jobs logged Emulator was not ready within 400s and started a new emulator, although each emulator had been ready about ten seconds after it started. localstack wait waits for a log line reading exactly Ready., and the emulator prints that marker in two writes (print(constants.READY_MARKER_OUTPUT, flush=True) in localstack-pro's localstack-core/localstack/runtime/runtime.py) while the Azurite container of its managed storage account logs from another thread, so a line can land between them: Ready.2026-10-13T09:30:02.013 DEBUG --- [ Thread-4] .... All 27 timeouts in five full runs between 2026-09-18 and 2026-10-13 (23 jobs) were this. A longer timeout would only make each one longer. lstk polls /_localstack/health and never reads the marker.
  • url-shortener/python/terraform fails with 409 PublicAccessNotPermitted when it creates its qrcodes container. The container is publicly readable (container_access_type = "blob"), but azurerm 5.x creates storage accounts with allow_nested_items_to_be_public = false unless told otherwise (provider source), and "If anonymous access is denied for the storage account, you can't configure anonymous access for a container" (Configure anonymous read access for containers and blobs). The emulator enforces this since localstack-pro#9198 (2026-10-07): the sample passed in the last full run before it and failed in the first one after. The Azure CLI and Bicep variants of the sample already allow public blob access on the account.
  • The retry of a failed Terraform sample cannot succeed. The failed attempt leaves its Terraform state behind, and the retry runs on a fresh emulator, so terraform plan first refreshes resources that no longer exist. A storage table cannot find its account, and the url-shortener retry failed with 404 ResourceNotFound before deploying anything.

Fixes SMF-939

Changes

  • lstk instead of the localstack CLI. The Start, Retry and Get LocalStack Logs steps of run-samples.yml run lstk start, lstk stop and lstk logs instead of python -m localstack_cli.cli.main and localstack logs, and the emulator container is now localstack-azure. run-samples.sh starts the emulator with lstk start, which returns at once in CI where the workflow has already started it, and no longer requires the localstack CLI. make logs runs lstk logs --verbose, and the localstack package leaves requirements-runtime.txt and pyproject.toml.
  • Guides. The sample READMEs and docs/LOCALSTACK.md start the emulator with lstk start --type azure, which returns once the emulator is ready, instead of localstack start and localstack wait. Where a prerequisites list named LocalStack, it now names the lstk CLI. docs/LOCALSTACK.md stops the emulator with lstk stop and reads its logs with lstk logs. No file in the repository runs the old CLI any more.
  • lstk version. lstk is installed before the emulator starts and moves from 0.19.0 to 1.4.0. The 0.19.0 pin avoided a 0.20.0 regression in which lstk picked the socket of a co-installed Podman instead of Docker's and reported the running emulator as absent; 0.20.1 fixed it (localstack/lstk#422). DOCKER_HOST now names the Docker daemon for every lstk call in the job.
  • Emulator settings. The variables in DOCKER_FLAGS and the bare LS_LOG, DISABLE_EVENTS and DNS_ADDRESS become LOCALSTACK_-prefixed variables, which lstk forwards to the container and the image's entrypoint strips. ACTIVATE_PRO goes: nothing in the image reads it.
  • Emulator logs. The container removes itself, logs included, when it exits, so the Start and Retry steps keep a copy of every emulator's logs, and Get LocalStack Logs prints it. A retried job now shows the emulator that failed as well as the one that ran the retry.
  • Retry. run-samples.sh removes a Terraform sample's terraform.tfstate, its backup and tfplan before deploying it.
  • url-shortener/python/terraform. The storage account sets allow_nested_items_to_be_public from a new storage_allow_nested_items_to_be_public variable that defaults to true.
  • .claude/settings.json is removed: it held one developer's local Claude Code permissions, with absolute paths to a local checkout.

Tests

Each changed step of run-samples.yml replayed locally from the workflow file (the step's run: block with its env:, a fresh HOME, lstk 1.4.0, localstack/localstack-azure:latest of 2026-10-13):

  • url-shortener/python/scripts: Start, Run and Retry pass, and the emulator reports ready within seconds.
  • A well-formed auth token that the license server does not know: each attempt of the Start step stops with License activation failed! and the license server's reason (exit code 55), instead of Error: timeout.
  • url-shortener/python/terraform: before the fixes, the first attempt reproduces the 409 PublicAccessNotPermitted; with only the sample fix, the retry over the leftover state reproduces the 404 ResourceNotFound; with both, the retry deploys from scratch (27 resources) and validate.sh passes. The committed workflow then passes Start, Run, Retry and Get LocalStack Logs, which prints the logs of both emulators.
  • The new Setup block of samples/url-shortener/python/README.md, run in a terminal on a machine with no lstk configuration: lstk start --type azure returns once the emulator is running, az group create reaches the emulator after lstk az start-interception, and lstk stop stops it.
  • actionlint, zizmor and gitleaks pass.

The CI of this PR runs all 91 jobs, and is the first time lstk starts the emulator on the arm64 runners.

Related

🤖 Generated with Claude Code

paolosalvatori and others added 5 commits October 13, 2026 10:09
…ck CLI

The legacy `localstack` CLI is deprecated and no longer receives
updates. Samples CI now drives the emulator with lstk end to end:

- The Start, Retry and Get LocalStack Logs steps use lstk start, stop
  and logs. The container is now named localstack-azure.
- Container settings move from DOCKER_FLAGS and bare variables to
  LOCALSTACK_-prefixed ones, which lstk forwards and the image's
  entrypoint strips. ACTIVATE_PRO goes: nothing in the image reads it.
- lstk is installed before the emulator starts and moves from 0.19.0
  to 1.4.0. The Podman detection regression behind the 0.19.0 pin was
  fixed in 0.20.1 (localstack/lstk#422), and DOCKER_HOST now pins the
  daemon for every lstk call in the job.
- The container removes itself, logs included, when it exits. The
  Start and Retry steps keep a copy of its logs, which Get LocalStack
  Logs prints when the emulator is gone, as the old CLI did itself.
- run-samples.sh and `make logs` use lstk, and the localstack package
  leaves requirements-runtime.txt: nothing imports it.

This does not fix the current "License activation failed" (exit 55).
The license server rejects TEST_LOCALSTACK_AUTH_TOKEN itself, and lstk
hands the token to the container exactly as the old CLI did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Terraform sample that fails leaves its state behind: run-samples.sh
removes it only after a successful run. The CI retry then deploys the
sample again on a fresh emulator, and terraform plan first refreshes
the old state there. Data-plane resources fail that refresh instead of
dropping out of state - a storage table cannot find its account - so
the retry failed before deploying anything, whatever had failed the
first attempt (url-shortener/python/terraform in run 38376597563).

run-samples.sh now removes terraform.tfstate, its backup and tfplan
before a Terraform sample deploys. The provider cache in .terraform is
kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…timeouts

The Start step's comments explained the expired readiness waits as
stalled boots. They were not: every "Emulator was not ready within
400s" warning in five full Samples CI runs between 2026-09-18 and
2026-10-13 - 27 timeouts in 23 jobs - was a ready emulator the old
localstack CLI failed to notice. The CLI waits for a log line reading
exactly "Ready.". The emulator prints that marker in two writes, and
the Azurite container of its managed storage account logs from another
thread at the same moment, so a line can land between them:
"Ready.2026-10-13T09:30:02.013 DEBUG ...". Each timed-out emulator had
been ready about ten seconds after it started.

lstk polls the health endpoint and never reads that marker, so the move
to lstk removes these false timeouts; a longer timeout would only have
made each of them longer. The comments now describe only what lstk
does.

Get LocalStack Logs now prints the copy of the logs that the Start and
Retry steps keep, which covers every emulator the job ran. On a retried
job, make logs showed only the retry's emulator, so the logs of the
emulator that failed were never printed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The qrcodes container is publicly readable (container_access_type =
"blob"), but azurerm 5.x creates storage accounts with
allow_nested_items_to_be_public = false unless told otherwise, and
Azure does not let a container grant anonymous access on an account
that denies it: creating the container fails with 409
PublicAccessNotPermitted. The emulator enforces this since
localstack-pro #9198 (2026-10-07). url-shortener/python/terraform passed
in the last full Samples CI run before it and failed in the first one
after. The scripts and Bicep variants already allow public blob access
on the account.

The setting is a variable with a default of true, like the sample's
other settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@paolosalvatori
paolosalvatori requested a review from a team as a code owner October 13, 2026 10:38
Copilot AI balanced review requested due to automatic review settings October 13, 2026 10:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Sample documentation still instructs users to run the removed localstack CLI.

1 open finding
What changed in this PR

Migrates sample CI from the deprecated localstack CLI to lstk, improves retries and logging, and fixes public blob access.

Changes:

  • Uses lstk for emulator lifecycle and diagnostics.
  • Resets Terraform state before retries.
  • Enables public QR-code blob access.
File Description
.github/​workflows/​run-samples.yml Migrates CI emulator management to lstk.
.claude/​settings.json Removes developer-local settings.
Makefile Uses lstk for logs.
requirements-runtime.txt Removes the deprecated CLI dependency.
run-samples.sh Starts with lstk and resets Terraform state.
samples/​url-shortener/​python/​terraform/​main.tf Enables configurable public blob access.
samples/​url-shortener/​python/​terraform/​variables.tf Defines the public-access setting.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread requirements-runtime.txt
…toml

The sample READMEs and docs/LOCALSTACK.md started the emulator with the
deprecated localstack CLI (IMAGE_NAME=... localstack start -d, then
localstack wait), while 45 of them listed only lstk among their
prerequisites, so a reader who followed them had no localstack command.
They now run lstk start --type azure, which returns once the emulator
is ready, and list the lstk CLI, linked to its documentation, where they
listed LocalStack. docs/LOCALSTACK.md also stops the emulator with lstk
stop and reads its logs with lstk logs.

pyproject.toml no longer depends on the localstack package, and the
workflow no longer mentions the old CLI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants