Skip to content

Commit 791ffbf

Browse files
authored
refactor build process, build onefile, upgrade to v2.1.0, add homebrew release (#8)
1 parent 26b0182 commit 791ffbf

File tree

8 files changed

+280
-57
lines changed

8 files changed

+280
-57
lines changed

.github/workflows/build.yml

Lines changed: 151 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Build with pyinstaller
1+
name: Build / Release
22

33
on:
44
pull_request:
@@ -7,44 +7,175 @@ on:
77
push:
88
branches:
99
- main
10+
tags:
11+
- '*'
12+
workflow_dispatch:
1013

1114
jobs:
12-
release:
13-
runs-on: ${{ matrix.os }}
14-
15+
build:
1516
strategy:
1617
matrix:
17-
os: [macos-10.15, windows-latest, ubuntu-latest]
18+
include:
19+
- runner: macos-11
20+
os: darwin
21+
arch: amd64
22+
- runner: windows-2019
23+
os: windows
24+
arch: amd64
25+
- runner: ubuntu-20.04
26+
os: linux
27+
arch: amd64
28+
- runner: buildjet-2vcpu-ubuntu-2204-arm
29+
os: linux
30+
arch: arm64
1831

32+
runs-on: ${{ matrix.runner }}
33+
outputs:
34+
cli_version: ${{ steps.cli_version.outputs.cli_version }}
1935
steps:
2036
- name: Check out Git repository
21-
uses: actions/checkout@v1
37+
uses: actions/checkout@v3
38+
39+
- name: Setup Python (GitHub Runner)
40+
if: ${{ !contains(matrix.runner, 'buildjet') }}
41+
uses: actions/setup-python@v4
42+
with:
43+
python-version: '3.10.11'
44+
45+
- name: Setup Python (BuildJet Runner)
46+
if: contains(matrix.runner, 'buildjet')
47+
uses: gabrielfalcao/pyenv-action@v14
48+
with:
49+
default: '3.10.11'
2250

2351
- name: Create virtual environment
2452
run: make venv
2553

2654
- name: Build using pyinstaller
27-
run: make build
55+
shell: bash
56+
run: make clean all
2857

29-
- name: Smoke test
58+
- name: Setup Docker on MacOS
59+
if: matrix.os == 'darwin'
3060
run: |
31-
ls dist/localstack
32-
dist/localstack/localstack --help
33-
dist/localstack/localstack config show
61+
brew install docker
62+
colima start
63+
64+
- name: Non-Docker Smoke tests
65+
shell: bash
66+
run: |
67+
ls dist-bin/
68+
cd dist-bin
69+
# show the help
70+
./localstack --help
71+
# show the config
72+
./localstack config show
73+
74+
- name: Community Docker Smoke tests (Linux, MacOS)
75+
shell: bash
76+
# GitHub Windows runner cannot run Docker containers (https://github.com/orgs/community/discussions/25491)
77+
# Skip these checks for forks (forks do not have access to the LocalStack Pro API key)
78+
if: matrix.os != 'windows' && !github.event.pull_request.head.repo.fork
79+
run: |
80+
# Pull images to avoid making smoke tests vulnerable to system behavior (docker pull speed)
81+
docker pull localstack/localstack
82+
cd dist-bin
83+
# start community
84+
./localstack start -d
85+
./localstack wait -t 60
86+
./localstack status services --format plain
87+
./localstack status services --format plain | grep "s3=available"
88+
./localstack stop
89+
90+
- name: Pro Docker Smoke tests (Linux, MacOS)
91+
shell: bash
92+
# GitHub Windows runner cannot run Docker containers (https://github.com/orgs/community/discussions/25491)
93+
# Skip these checks for forks (forks do not have access to the LocalStack Pro API key)
94+
if: matrix.os != 'windows' && !github.event.pull_request.head.repo.fork
95+
run: |
96+
# Pull images to avoid making smoke tests vulnerable to system behavior (docker pull speed)
97+
docker pull localstack/localstack-pro
98+
cd dist-bin
99+
# start pro
100+
LOCALSTACK_API_KEY=${{ secrets.TEST_LOCALSTACK_API_KEY }} ./localstack start -d
101+
./localstack wait -t 60
102+
./localstack status services --format plain
103+
./localstack status services --format plain | grep "xray=available"
104+
./localstack stop
34105
35106
- name: Set CLI version output
36107
id: cli_version
108+
shell: bash
109+
run: |
110+
dist-bin/localstack --version
111+
echo "cli_version=$(dist-bin/localstack --version)" >> $GITHUB_OUTPUT
112+
113+
- name: Archive distribution (Linux, MacOS)
114+
if: matrix.os != 'windows'
115+
run: |
116+
cd dist-bin/
117+
tar -czf ${{github.event.repository.name}}-${{steps.cli_version.outputs.cli_version}}-${{ matrix.os }}-${{ matrix.arch }}-onefile.tar.gz localstack
118+
rm localstack
119+
cd ../dist-dir/
120+
tar -czf ${{github.event.repository.name}}-${{steps.cli_version.outputs.cli_version}}-${{ matrix.os }}-${{ matrix.arch }}.tar.gz localstack
121+
rm -r localstack
122+
123+
- name: Archive distribution (Windows)
124+
if: matrix.os == 'windows'
125+
run: |
126+
cd dist-bin/
127+
Compress-Archive localstack.exe ${{github.event.repository.name}}-${{steps.cli_version.outputs.cli_version}}-${{ matrix.os }}-${{ matrix.arch }}.zip
128+
rm localstack.exe
129+
cd ../dist-dir/
130+
Compress-Archive localstack ${{github.event.repository.name}}-${{steps.cli_version.outputs.cli_version}}-${{ matrix.os }}-${{ matrix.arch }}-onefile.zip
131+
rm -r localstack
132+
133+
- name: Upload binary artifacts
134+
uses: actions/upload-artifact@v3
135+
with:
136+
name: ${{github.event.repository.name}}-${{steps.cli_version.outputs.cli_version}}-${{ matrix.os }}-${{ matrix.arch }}-onefile
137+
path: 'dist-bin/*'
138+
139+
- name: Upload folder artifacts
140+
uses: actions/upload-artifact@v3
141+
with:
142+
name: ${{github.event.repository.name}}-${{steps.cli_version.outputs.cli_version}}-${{ matrix.os }}-${{ matrix.arch }}
143+
path: 'dist-dir/*'
144+
145+
release:
146+
runs-on: ubuntu-latest
147+
if: startsWith(github.ref, 'refs/tags/')
148+
needs:
149+
- build
150+
permissions:
151+
contents: write
152+
steps:
153+
- name: Download Builds
154+
uses: actions/download-artifact@v3
155+
with:
156+
path: builds
157+
158+
# TODO remove this section once we have native darwin arm64 builds
159+
# This step is currently necessary for the homebrew action to pick up the MacOS AMD64 package for MacOS ARM64 / M1
160+
# GitHub Roadmap: https://github.com/github/roadmap/issues/528
161+
- name: (Intermediate) Use Darwin AMD64 for Darwin ARM64
37162
run: |
38-
dist/localstack/localstack --version
39-
echo "::set-output name=number::$(dist/localstack/localstack --version)"
163+
cp ./builds/${{github.event.repository.name}}-${{needs.build.outputs.cli_version}}-darwin-amd64/* ./builds/${{github.event.repository.name}}-${{needs.build.outputs.cli_version}}-darwin-amd64/${{github.event.repository.name}}-${{needs.build.outputs.cli_version}}-darwin-arm64.tar.gz
164+
cp ./builds/${{github.event.repository.name}}-${{needs.build.outputs.cli_version}}-darwin-amd64-onefile/* ./builds/${{github.event.repository.name}}-${{needs.build.outputs.cli_version}}-darwin-amd64-onefile/${{github.event.repository.name}}-${{needs.build.outputs.cli_version}}-darwin-arm64-onefile.tar.gz
40165
41-
- name: Archive distribution
166+
- name: Generate Checksums
42167
run: |
43-
cd dist/
44-
tar -czf localstack-cli-${{steps.cli_version.outputs.number}}-${{ matrix.os }}.tar.gz localstack
168+
# move all files from the builds subdirectories to the builds root folder
169+
find ./builds/ -type f -print0 | xargs -0 mv -t ./builds/
170+
# remove all (empty) subdirectories
171+
find ./builds/ -mindepth 1 -maxdepth 1 -type d -print0 | xargs -r0 rm -R
172+
# generate the checksums
173+
cd builds
174+
sha256sum *.{tar.gz,zip} > ${{github.event.repository.name}}-${{needs.build.outputs.cli_version}}-checksums.txt
45175
46-
- name: Upload artifacts
47-
uses: actions/upload-artifact@v2
176+
- name: Release
177+
uses: softprops/action-gh-release@v1
48178
with:
49-
name: localstack-cli-${{steps.cli_version.outputs.number}}-${{ matrix.os }}
50-
path: dist/localstack-cli-${{steps.cli_version.outputs.number}}-${{ matrix.os }}.tar.gz
179+
files: 'builds/*'
180+
draft: true
181+
token: ${{ secrets.LOCALSTACK_GITHUB_TOKEN }}

.github/workflows/homebrew.yml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Release Homebrew Tap
2+
3+
on:
4+
release:
5+
# Start Homebrew Releaser when a new GitHub release of the CLI package is _published_
6+
types: [published]
7+
8+
jobs:
9+
homebrew-releaser:
10+
runs-on: ubuntu-latest
11+
name: homebrew-releaser
12+
steps:
13+
- name: Add published release to Homebrew Tap
14+
uses: Justintime50/homebrew-releaser@v1
15+
with:
16+
# The name of the homebrew tap to publish your formula to as it appears on GitHub.
17+
# Required - strings
18+
homebrew_owner: localstack
19+
homebrew_tap: homebrew-tap
20+
21+
# Logs debugging info to console.
22+
# Default is shown - boolean
23+
debug: true
24+
25+
# The name of the folder in your homebrew tap where formula will be committed to.
26+
# Default is shown - string
27+
formula_folder: Formula
28+
29+
# The Personal Access Token (saved as a repo secret) that has `repo` permissions for the repo running the action AND Homebrew tap you want to release to.
30+
# Required - string
31+
github_token: ${{ secrets.LOCALSTACK_GITHUB_TOKEN }}
32+
33+
34+
# Git author info used to commit to the homebrew tap.
35+
# Defaults are shown - strings
36+
commit_owner: localstack-bot
37+
commit_email: 88328844+localstack-bot@users.noreply.github.com
38+
39+
# Custom install command for your formula.
40+
# Required - string
41+
# The indentation is on purpose to fix the multiline indentation in the final formula
42+
install: |
43+
libexec.install Dir["*"]
44+
bin.install_symlink libexec/"localstack"
45+
46+
# Custom test command for your formula so you can run `brew test`.
47+
# Optional - string
48+
test: |
49+
assert_match /LocalStack Command Line Interface/, shell_output("#{bin}/localstack --help", 0)
50+
51+
# Adds URL and checksum targets for different OS and architecture pairs. Using this option assumes
52+
# a tar archive exists on your GitHub repo with the following URL pattern (this cannot be customized):
53+
# https://github.com/{GITHUB_OWNER}/{REPO_NAME}/releases/download/{TAG}/{REPO_NAME}-{VERSION}-{OPERATING_SYSTEM}-{ARCHITECTURE}.tar.gz'
54+
# Darwin AMD pre-existing path example: https://github.com/justintime50/myrepo/releases/download/v1.2.0/myrepo-1.2.0-darwin-amd64.tar.gz
55+
# Linux ARM pre-existing path example: https://github.com/justintime50/myrepo/releases/download/v1.2.0/myrepo-1.2.0-linux-arm64.tar.gz
56+
# Optional - booleans
57+
target_darwin_amd64: true
58+
target_darwin_arm64: true
59+
target_linux_amd64: true
60+
target_linux_arm64: true

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# Build dist folders
2+
dist-bin
3+
dist-dir
4+
5+
# IntelliJ
16
.idea/
27
*.iml
38
*~

Makefile

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
VENV_BIN = python3 -m venv
22
VENV_DIR ?= .venv
3+
PYINSTALLER_ARGS = --distpath=dist-bin --onefile
34

45
ifeq ($(OS), Windows_NT)
56
VENV_ACTIVATE = $(VENV_DIR)/Scripts/activate
@@ -9,7 +10,7 @@ endif
910

1011
VENV_RUN = . $(VENV_ACTIVATE)
1112

12-
all: venv build
13+
all: dist-bin/localstack dist-dir/localstack
1314

1415
venv: $(VENV_ACTIVATE)
1516

@@ -19,23 +20,22 @@ $(VENV_ACTIVATE): requirements.txt
1920
$(VENV_RUN); pip install -r requirements.txt
2021
touch $(VENV_ACTIVATE)
2122

22-
# currently botocore and boto3 are required because patch.py of localstack-client>=1.33
23-
# once that is remedied, we can exclude boto3 and botocore modules again
24-
dist/localstack/localstack: main.py
23+
dist-bin/localstack build: $(VENV_ACTIVATE) main.py
2524
$(VENV_RUN); pyinstaller main.py \
2625
$(PYINSTALLER_ARGS) -n localstack \
27-
--exclude-module moto \
28-
--hidden-import docker
29-
rm -rf dist/localstack/botocore/data
26+
--hidden-import localstack_ext.cli.localstack \
27+
--additional-hooks-dir hooks
3028

31-
build: venv dist/localstack/localstack
29+
dist-dir/localstack: PYINSTALLER_ARGS=--distpath=dist-dir
30+
dist-dir/localstack: $(VENV_ACTIVATE) main.py build
3231

3332
clean:
3433
rm -rf build/
35-
rm -rf dist/
34+
rm -rf dist-bin/
35+
rm -rf dist-dir/
3636

3737
clean-venv:
3838
rm -rf $(VENV_DIR)
3939

40-
.PHONY: clean clean-venv
40+
.PHONY: all build clean clean-venv
4141

README.md

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,56 @@
1-
localstack-packaged-cli
1+
LocalStack CLI
22
=======================
33

4-
Repository for the build config that packages the localstack cli into a standalone binary.
5-
6-
## Build
7-
4+
This repository contains building instructions for binary builds of the LocalStack CLI.
5+
It does not contain the actual source for the CLI, since the LocalStack CLI is basically just the Python package `localstack` (published on PyPi) with it's install dependencies (and without any extras).
6+
This is why this repository just contains the build config and pipeline that packages the LocalStack CLI python package into a standalone binary using PyInstaller.
7+
8+
## Creating a Release
9+
In order to create a release, just perform the following tasks:
10+
- Create a commit which sets a new explicit version for `localstack` in the `requirements.txt`.
11+
- For example: `localstack==2.1.0`
12+
- Create a tag for the commit: `v<version>`.
13+
- For example: `git tag v2.1.0`
14+
- Push the tag (`git push origin v<version>`)
15+
- This will trigger the following actions:
16+
- The tag will trigger the ["Build / Release"](.github/workflows/build.yml) GitHub workflow.
17+
- It will build the binaries for the different systems and create a GitHub release draft.
18+
- Publish the GitHub release draft.
19+
- This will trigger the ["Release Homebrew Tap"](.github/workflows/homebrew.yml) GitHub workflow.
20+
- It will take the release artifacts and update the Homebrew formula in [localstack/homebrew-tap](https://github.com/localstack/homebrew-tap).
21+
22+
### Dev Releases
23+
If a dev release is created, the tag name has to have the same name as the version of `localstack-core` being used (because this is the output of `localstack --version`).
24+
Otherwise, the ["Release Homebrew Tap"](.github/workflows/homebrew.yml) GitHub workflow will not be able to find the artifacts.
25+
26+
## Manual Build
827
### python3-dev
9-
1028
You need Python developer version libraries in your path to be able to build the distribution.
11-
12-
For most of us who use pyenv, this is done with
13-
14-
```bash
15-
pyenv install 3.8-dev
16-
pyenv local 3.8-dev
29+
For most of us who use pyenv, this is done with:
30+
- MacOS:
31+
```bash
32+
env PYTHON_CONFIGURE_OPTS="--enable-framework" pyenv install 3.10-dev
33+
```
34+
- Linux:
35+
```bash
36+
env PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install 3.10-dev
37+
```
38+
39+
Activate the version:
40+
```
41+
pyenv local 3.10-dev
1742
python --version
1843
```
44+
This should print something like `Python 3.10.11+`.
1945

20-
should print something like `Python 3.8.15+`.
21-
22-
### make all
23-
24-
Just run
25-
46+
### Building
47+
You can build the specific versions by calling the respective make target:
2648
```bash
49+
make clean dist-bin/localstack
50+
# or:
51+
make clean dist-dir/localstack
52+
# or both:
2753
make clean all
2854
```
29-
30-
in `dist/localstack` you should now find the binary assets.
31-
32-
If you want a single binary you can run `PYINSTALLER_ARGS=-F make clean all`.
33-
This will create a single binary `dist/localstack`.
55+
You can find the binary assets in `dist-bin/` and `dist-dir`.
3456
The single binary has a slower startup time than the binary distribution.

0 commit comments

Comments
 (0)