Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
name: Release LocalStack Python Client

on:
repository_dispatch:
types: [release-sdk]
workflow_dispatch:
inputs:
version:
description: The version of the OpenAPI spec to release the client for
required: true

env:
git_user_name: localstack[bot]
git_user_email: localstack-bot@users.noreply.github.com

jobs:

test_python:
runs-on: ubuntu-latest
env:
release: ${{ github.event_name == 'workflow_dispatch' && inputs.version || github.event.client_payload.version}}

steps:
- name: "Set the URL of the spec"
run: |
echo "SPEC_URL=https://github.com/localstack/openapi/releases/download/v${{ env.release }}/localstack-spec.yml" >> $GITHUB_ENV

- name: "Verify OpenAPI version"
run: |
# Check if the URL is valid
if ! wget --spider -q "${SPEC_URL}"; then
echo "Invalid spec to build from: ${SPEC_URL}. Aborting ..."
exit 1
fi

- name: "Pull image"
run: |
docker pull localstack/localstack-pro:${{ env.release }}

- name: "Checkout"
uses: actions/checkout@v4
with:
# setuptools_scm requires git history to determine the version
fetch-depth: 0

- name: "Set up Python 3.11"
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: "Install uv"
uses: astral-sh/setup-uv@v3

- name: "Generate code from spec"
run: |
make clean-generated
./bin/generate.sh ${SPEC_URL}
Copy link
Member

Choose a reason for hiding this comment

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

nitpick (non-blocking): ‏Maybe it would be good to have all the "spec url fiddling" in the script itself? I think it would be nicer / easier to use if the script just takes an optional version argument and in the script it either uses the latest spec or constructs the URL based on the version. What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for the suggestion 👍 definitely cleaner, and we could also drop 2 steps from the workflow. Done in 892bf4d


- name: "Prepare git config"
run: |
git config user.name ${{ env.git_user_name }}
git config user.email ${{ env.git_user_email }}

- name: "Commit changed code"
run: |
if git status --porcelain packages/ | grep -q '^'; then
Copy link
Member

Choose a reason for hiding this comment

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

nitpick (non-blocking): ‏That's an interesting condition, could you maybe add a comment here to explain what's going on in this line?

git add packages/
git commit -m "Generate code for ${{ env.release }}"

echo "Changes committed successfully"
else
echo "No changes detected after generating the code"
fi

- name: "Install project"
run: |
make install-dev

- name: "Install LocalStack"
run: |
pip install localstack==${{ env.release }}
Comment on lines +69 to +71
Copy link
Member

Choose a reason for hiding this comment

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

nitpick (non-blocking): The installation of the right version of localstack is just defined here in the pipeline. But it seems there is also a requirements-spec.txt (which defines localstack as a dependency, but might not be used at all anymore?). I think it would be nice to:

  • Clean up / remove the requirements file
  • Either extend the readme explaining that one needs to start the right version of LocalStack manually, or integrate an automatic startup of the right version of LocalStack into the pytest conftest / setup.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch. The requirement file is just a leftover from previous iterations.


- name: "Start Localstack"
env:
LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }}
run: |
source .venv/bin/activate
DEBUG=1 DISABLE_EVENTS="1" IMAGE_NAME="localstack/localstack-pro:${{ env.release }}" localstack start -d
localstack wait -t 120 || (python -m localstack.cli.main logs && false)

- name: "Run Python Tests"
env:
LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }}
run: |
make test

- name: "Stop Localstack"
if: always()
run: |
source .venv/bin/activate
localstack logs
localstack stop

- name: "Install release helper"
run: |
curl -o bin/release-helper.sh -L https://api.github.com/repos/localstack/localstack/contents/bin/release-helper.sh -H 'Accept: application/vnd.github.v3.raw'
chmod +x bin/release-helper.sh

- name: "Create the release commit and tag"
run: |
bin/release-helper.sh git-commit-release ${{ env.release }}

- name: "Publish release to pypi"
run: |
make clean && make install
make publish
with:
UV_PUBLISH_TOKEN: ${{ secrets.UV_PUBLISH_TOKEN }}

- name: "Push the release commit and tag"
run: |
git push --follow-tags

- name: "Create GitHub release"
env:
GITHUB_TOKEN: ${{ secrets.LOCALSTACK_GITHUB_TOKEN }}
run: gh release create "${{ env.release }}" --generate-notes --draft

- name: "Commit and push next development version"
run: |
bin/release-helper.sh git-commit-increment
git push

- name: "Publish development version to pypi"
run: |
make install && make publish
with:
UV_PUBLISH_TOKEN: ${{ secrets.UV_PUBLISH_TOKEN }}

- name: "Show git modifications"
run: |
git log --oneline -n 4
git show HEAD~1
git show HEAD
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,6 @@ localstack-sdk-python-2/.openapi-generator/

# setuptools_scm version.py
**/version.py

# release helper
bin/release-helper.sh
11 changes: 10 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,21 @@ clean: ## Clean up the virtual environment
rm -rf $(VENV_DIR)
rm -rf dist/

clean-dist:
rm -rf dist/

clean-generated: ## Cleanup generated code
rm -rf packages/localstack-sdk-generated/localstack/

generate: ## Generate the code from the OpenAPI specs
./bin/generate.sh

build:
uv build

publish: clean-dist build
uv publish

format:
($(VENV_RUN); python -m ruff format --exclude packages .; python -m ruff check --output-format=full --exclude packages --fix .)

Expand All @@ -32,4 +41,4 @@ lint:
test: ## Run automated tests
($(VENV_RUN); $(TEST_EXEC) pytest --durations=10 --log-cli-level=$(PYTEST_LOGLEVEL) $(PYTEST_ARGS) $(TEST_PATH))

.PHONY: clean install install-dev
.PHONY: clean install install-dev clean clean-dist clean-generated generate build publish format lint test
11 changes: 10 additions & 1 deletion bin/generate.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
#!/bin/bash

LATEST_SPEC="https://raw.githubusercontent.com/localstack/openapi/refs/heads/main/openapi/emulators/localstack-spec-latest.yml"
SPEC_URL="${1:-$LATEST_SPEC}"

# Check if the URL is valid
if ! wget --spider -q "$SPEC_URL"; then
echo "Spec URL seems not accessible: $SPEC_URL"
exit 1
fi

docker run --rm -v "${PWD}:/local" openapitools/openapi-generator-cli:v7.10.0 generate \
-i https://raw.githubusercontent.com/localstack/openapi/refs/heads/main/openapi/emulators/localstack-spec-latest.yml \
-i "$SPEC_URL" \
--skip-validate-spec \
-g python \
-o /local//packages/localstack-sdk-generated \
Expand Down