Skip to content

Commit 6feb525

Browse files
authored
fix: docker references and test (#59)
* fix: docker references and test * test * test * test * test * fix: test * fix: test * fix: test * fix: test * fix: checksum calculation, docker image, test * fix: path * fix: install curl * fix: dockerfile * fix: test * fix: path to app * fix: dockerfile * test * test * test * test * test * test * test * test * remove sleep * fix: error output * fix: all * docs: update info on README * docs: fix readme
1 parent e3db0e5 commit 6feb525

19 files changed

Lines changed: 144 additions & 88 deletions

.github/workflows/build.yml

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
paths:
66
- '.github/workflows/build.yml'
7+
- 'Dockerfile'
78
- '**/*.py'
89
branches:
910
- '**'
@@ -12,6 +13,7 @@ on:
1213
pull_request:
1314
paths:
1415
- '.github/workflows/build.yml'
16+
- 'Dockerfile'
1517
- '**/*.py'
1618
workflow_dispatch: ~
1719

@@ -49,11 +51,20 @@ jobs:
4951
runs-on: ubuntu-latest
5052
steps:
5153
- uses: actions/checkout@v4
52-
- uses: actions/setup-python@v5
53-
with:
54-
python-version: '3.13'
55-
- name: Build Docker image
56-
run: docker build . -t justintime50/homebrew-releaser
54+
- name: Build and run Docker image
55+
run: |
56+
docker build . -t justintime50/homebrew-releaser
57+
docker run --workdir /github/workspace \
58+
-e INPUT_SKIP_COMMIT=true \
59+
-e INPUT_DEBUG=true \
60+
-e GITHUB_REPOSITORY=justintime50/homebrew-releaser \
61+
-e INPUT_HOMEBREW_OWNER=justintime50 \
62+
-e INPUT_HOMEBREW_TAP=homebrew-formulas \
63+
-e INPUT_GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \
64+
-e INPUT_INSTALL=virtualenv_install_with_resources \
65+
-e INPUT_FORMULA_INCLUDES="include Language::Python::Virtualenv" \
66+
-e INPUT_UPDATE_PYTHON_RESOURCES=true \
67+
justintime50/homebrew-releaser
5768
coverage:
5869
if: github.ref == 'refs/heads/main'
5970
runs-on: ubuntu-latest

CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
# CHANGELOG
22

3-
## v2.1.1 (2025-06-13)
3+
## v2.1.1 (2025-06-14)
44

5-
- Fixes Dockerfile references so Python/Git can access what it needs
5+
- Corrects Python references in Dockerfile
6+
- Uses `/tmp` as working directory due to permissioning issues
7+
- Drop `shasum` dependency and calculate checksums from inside Python
8+
- Bumps `TIMEOUT` from 60 to 300 seconds for larger projects (downloading Python packages can take some time)
69

710
## v2.1.0 (2025-06-10)
811

12+
> DO NOT use this image as it cannot build properly.
13+
914
- Changes Docker image from `python3.13` to `brew` allowing us to use all brew tools during builds
1015
- Adds `update_python_resources` so Python formula can update their required resources
1116
- Bumps `TIMEOUT` from 30 to 60 seconds for larger projects

Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ COPY --chown=linuxbrew:linuxbrew homebrew_releaser homebrew_releaser
44
COPY --chown=linuxbrew:linuxbrew setup.py setup.py
55

66
RUN brew install python@3.13 \
7-
&& python3 -m venv venv \
8-
&& venv/bin/pip install .
7+
&& python3 -m venv /home/linuxbrew/venv \
8+
&& /home/linuxbrew/venv/bin/pip install .
99

10-
ENTRYPOINT [ "venv/bin/python3", "homebrew_releaser/app.py" ]
10+
ENTRYPOINT [ "/home/linuxbrew/venv/bin/python3", "/home/linuxbrew/homebrew_releaser/app.py" ]

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ jobs:
9595

9696
# Run 'brew update-python-resources' on the formula to add Python resources.
9797
# Docs: https://docs.brew.sh/Python-for-Formula-Authors#python-declarations-for-applications
98+
# NOTE: This only works with public repositories currently and must be done manually if for a private repo.
9899
# Default is shown - boolean
99100
update_python_resources: false
100101

@@ -143,13 +144,46 @@ jobs:
143144
debug: false
144145
```
145146
147+
#### Python
148+
149+
To release a Python package using Homebrew Releaser, one may setup their workflow like so:
150+
151+
```yml
152+
on:
153+
push:
154+
155+
jobs:
156+
homebrew-releaser:
157+
runs-on: ubuntu-latest
158+
name: homebrew-releaser
159+
steps:
160+
- name: Release my project to my Homebrew tap
161+
uses: Justintime50/homebrew-releaser@v2
162+
with:
163+
homebrew_owner: Justintime50
164+
homebrew_tap: homebrew-formulas
165+
github_token: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
166+
commit_owner: homebrew-releaser
167+
commit_email: homebrew-releaser@example.com
168+
169+
# Python specific variables
170+
depends_on: 'python@3.y'
171+
install: 'virtualenv_install_with_resources'
172+
formula_includes: 'include Language::Python::Virtualenv'
173+
update_python_resources: true
174+
```
175+
146176
## Development
147177
148178
```sh
149179
# Get a comprehensive list of development tools
150180
just --list
151181
```
152182

183+
### Docker & GitHub Actions Setup
184+
185+
We have to play a balancing game of using Homebrew (cannot be root) and writing various files to use Homebrew Releaser (must be root or have write access), to get around this we setup and install everything using a `linuxbrew` user and directory but then everything afterwards (git clone, writing files, etc) occur inside of `/tmp`. This is an unfortunate but required path to take since using the official Python Docker image won't let us use Brew and have write access while using the Brew image gets us Homebrew but we cannot write in the normal directory. Thus we hack it a bit and just play inside of `/tmp`.
186+
153187
### Run Manually via Docker
154188

155189
Homebrew Releaser does not clean up artifacts after completing since the temporary Docker image on GitHub Actions will be discarded anyway.

docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ services:
22
homebrew-releaser:
33
container_name: homebrew-releaser
44
build: .
5+
# Mimicks the GitHub Actions environment
6+
working_dir: /github/workspace
57
environment:
68
# Env vars that should be `false` should be left empty
79
- GITHUB_REPOSITORY=username/repo

homebrew_releaser/app.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def run_github_action():
131131
# For REST API requests, we should not stream archive file, but it is fine for browser URLs
132132
stream = False if archive_url.find("api.github.com") != -1 else True
133133
downloaded_filename = App.download_archive(download_url, stream)
134-
checksum = Checksum.get_checksum(downloaded_filename)
134+
checksum = Checksum.calculate_checksum(downloaded_filename)
135135
archive_filename = Utils.get_filename_from_path(archive_url)
136136
archive_checksum_entries += f'{checksum} {archive_filename}\n'
137137
checksums.append(
@@ -165,7 +165,7 @@ def run_github_action():
165165
)
166166

167167
formula_filename = f'{repository["name"]}.rb'
168-
formula_dir = os.path.join(HOMEBREW_TAP, FORMULA_FOLDER)
168+
formula_dir = Utils.get_working_dir(os.path.join(HOMEBREW_TAP, FORMULA_FOLDER))
169169
formula_filepath = os.path.join(formula_dir, formula_filename)
170170
Utils.write_file(formula_filepath, template, 'w')
171171

@@ -185,7 +185,7 @@ def run_github_action():
185185
else:
186186
logger.debug('Skipping update to project README.')
187187

188-
# Although users can skip a commit, still commit (and don't push) to dry-run a commit
188+
# Although users can skip a commit, still commit (but don't push) to dry-run the commit for debugging purposes
189189
Git.add(HOMEBREW_TAP)
190190
Git.commit(HOMEBREW_TAP, GITHUB_REPO, version)
191191

homebrew_releaser/checksum.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import subprocess # nosec
1+
import hashlib
22
from typing import Any
33

44
import requests
@@ -12,28 +12,20 @@
1212
LOGGER_NAME,
1313
TIMEOUT,
1414
)
15+
from homebrew_releaser.utils import Utils
1516

1617

1718
class Checksum:
1819
@staticmethod
19-
def get_checksum(tar_filepath: str) -> str:
20+
def calculate_checksum(tar_filepath: str) -> str:
2021
"""Gets the checksum of a file."""
2122
logger = woodchips.get(LOGGER_NAME)
2223

2324
try:
24-
command = ['shasum', '-a', '256', tar_filepath]
25-
output = subprocess.check_output( # nosec
26-
command,
27-
stdin=None,
28-
stderr=None,
29-
timeout=TIMEOUT,
30-
)
31-
checksum = output.decode().split()[0]
32-
checksum_filename = output.decode().split()[1]
33-
logger.debug(f'Checksum for {checksum_filename} generated successfully: {checksum}')
34-
except subprocess.TimeoutExpired as error:
35-
raise SystemExit(error)
36-
except subprocess.CalledProcessError as error:
25+
with open(Utils.get_working_dir(tar_filepath), "rb") as content:
26+
checksum = hashlib.sha256(content.read()).hexdigest()
27+
logger.debug(f'Checksum for {tar_filepath} generated successfully: {checksum}')
28+
except Exception as error:
3729
raise SystemExit(error)
3830

3931
return checksum
@@ -45,7 +37,7 @@ def upload_checksum_file(latest_release: dict[str, Any]):
4537

4638
latest_release_id = latest_release['id']
4739

48-
with open(CHECKSUM_FILE, 'rb') as filename:
40+
with open(Utils.get_working_dir(CHECKSUM_FILE), 'rb') as filename:
4941
checksum_file_content = filename.read()
5042

5143
upload_url = f'https://uploads.github.com/repos/{GITHUB_OWNER}/{GITHUB_REPO}/releases/{latest_release_id}/assets?name={CHECKSUM_FILE}' # noqa

homebrew_releaser/constants.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@
2020

2121
# App Constants
2222
LOGGER_NAME = 'homebrew-releaser'
23-
TIMEOUT = 60
23+
TIMEOUT = 300
2424
GITHUB_HEADERS = {
2525
'Accept': 'application/vnd.github.v3+json',
2626
'Agent': 'Homebrew Releaser',
2727
'Authorization': f'Bearer {GITHUB_TOKEN}',
2828
}
2929
CHECKSUM_FILE = 'checksum.txt'
30+
WORKING_DIR = '/tmp/homebrew-releaser' # nosec
3031

3132
# Formula Constants
3233
ARTICLES = {

homebrew_releaser/formula.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -282,16 +282,17 @@ def update_python_resources(formula_dir: str, formula_filename: str) -> None:
282282
raise SystemExit("brew not found in PATH")
283283

284284
try:
285-
logger.info(f'Running brew update-python-resources for {formula_filename}...')
286285
subprocess.check_output(
287286
f'cd {formula_dir} && {brew_path} update-python-resources {formula_filename}',
288287
stderr=subprocess.STDOUT,
289288
text=True,
290289
timeout=TIMEOUT,
291290
shell=True, # nosec
292291
)
293-
logger.info(f'Successfully updated Python resources for {formula_filename}')
294-
except subprocess.TimeoutExpired as e:
295-
raise SystemExit from e
292+
logger.info('Updated Python resources successfully.')
293+
except subprocess.TimeoutExpired:
294+
raise SystemExit('Timed out updating Python resources')
296295
except subprocess.CalledProcessError as e:
297-
raise SystemExit(f'An error occurred while updating Python resources: {e}') from e
296+
error_output = e.output if hasattr(e, "output") else ""
297+
298+
raise SystemExit(f"An error occurred while updating Python resources: {error_output}")

homebrew_releaser/git.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
LOGGER_NAME,
99
TIMEOUT,
1010
)
11+
from homebrew_releaser.utils import Utils
1112

1213

1314
class Git:
@@ -28,9 +29,10 @@ def setup(commit_owner: str, commit_email: str, homebrew_owner: str, homebrew_ta
2829
'clone',
2930
'--depth=1',
3031
f'https://x-access-token:{GITHUB_TOKEN}@github.com/{homebrew_owner}/{homebrew_tap}.git',
32+
Utils.get_working_dir(homebrew_tap),
3133
],
32-
['git', '-C', homebrew_tap, 'config', 'user.name', f'"{commit_owner}"'],
33-
['git', '-C', homebrew_tap, 'config', 'user.email', commit_email],
34+
['git', '-C', Utils.get_working_dir(homebrew_tap), 'config', 'user.name', f'"{commit_owner}"'],
35+
['git', '-C', Utils.get_working_dir(homebrew_tap), 'config', 'user.email', commit_email],
3436
]
3537

3638
for command in commands:
@@ -41,22 +43,22 @@ def setup(commit_owner: str, commit_email: str, homebrew_owner: str, homebrew_ta
4143
@staticmethod
4244
def add(homebrew_tap: str):
4345
"""Adds assets to a git commit."""
44-
command = ['git', '-C', homebrew_tap, 'add', '.']
46+
command = ['git', '-C', Utils.get_working_dir(homebrew_tap), 'add', '.']
4547
Git._run_git_subprocess(command, 'Assets added to git commit successfully.')
4648

4749
@staticmethod
4850
def commit(homebrew_tap: str, repo_name: str, version: str):
4951
"""Commits assets to the Homebrew tap (repo)."""
5052
# fmt: off
51-
command = ['git', '-C', homebrew_tap, 'commit', '-m', f'chore: brew formula update for {repo_name} {version}'] # noqa
53+
command = ['git', '-C', Utils.get_working_dir(homebrew_tap), 'commit', '-m', f'chore: brew formula update for {repo_name} {version}'] # noqa
5254
# fmt: on
5355
Git._run_git_subprocess(command, 'Assets committed successfully.')
5456

5557
@staticmethod
5658
def push(homebrew_tap: str, homebrew_owner: str):
5759
"""Pushes assets to the remote Homebrew tap (repo)."""
5860
# fmt: off
59-
command = ['git', '-C', homebrew_tap, 'push', f'https://x-access-token:{GITHUB_TOKEN}@github.com/{homebrew_owner}/{homebrew_tap}.git'] # noqa
61+
command = ['git', '-C', Utils.get_working_dir(homebrew_tap), 'push', f'https://x-access-token:{GITHUB_TOKEN}@github.com/{homebrew_owner}/{homebrew_tap}.git'] # noqa
6062
# fmt: on
6163
Git._run_git_subprocess(command, f'Assets pushed successfully to {homebrew_tap}.')
6264

0 commit comments

Comments
 (0)