Skip to content

Commit 2a81a3e

Browse files
committed
fix: harden CD workflow against script injection, add VERSION guard
1 parent 4c112e8 commit 2a81a3e

3 files changed

Lines changed: 52 additions & 26 deletions

File tree

.github/workflows/cd.yml

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ jobs:
4242
python-version: '3.11'
4343

4444
- name: Check that we're on main branch
45+
env:
46+
CURRENT_BRANCH: ${{ github.ref_name }}
4547
run: |
46-
# Use GitHub context instead of git command to avoid detached HEAD issues
47-
CURRENT_BRANCH="${{ github.ref_name }}"
4848
if [ "$CURRENT_BRANCH" != "main" ]; then
4949
echo "Error: Releases can only be created from the main branch. Current branch: $CURRENT_BRANCH"
5050
exit 1
@@ -182,10 +182,12 @@ jobs:
182182

183183
- name: Restore repository from bundle
184184
shell: bash
185+
env:
186+
TAG_NAME: ${{ needs.prepare-version.outputs.tag_name }}
185187
run: |
186188
git clone repo.bundle repo
187189
cd repo
188-
git checkout ${{ needs.prepare-version.outputs.tag_name }}
190+
git checkout "$TAG_NAME"
189191
190192
# Debug: verify we're in the right state
191193
echo "Current directory: $(pwd)"
@@ -206,6 +208,8 @@ jobs:
206208
- name: Install Dependencies (including PyInstaller)
207209
shell: bash
208210
working-directory: ./repo
211+
env:
212+
EXPECTED_VERSION: ${{ needs.prepare-version.outputs.version }}
209213
run: |
210214
python -m pip install --upgrade pip
211215
# Install in editable mode to ensure version.py changes are picked up
@@ -214,34 +218,39 @@ jobs:
214218
# Verify the installed version matches what we expect
215219
echo "Verifying installed version:"
216220
python -c "from treemapper.version import __version__; print(f'Version in module: {__version__}')"
217-
echo "Expected version: ${{ needs.prepare-version.outputs.version }}"
221+
echo "Expected version: ${EXPECTED_VERSION}"
218222
219223
- name: Build with PyInstaller
220224
shell: bash
221225
working-directory: ./repo
226+
env:
227+
ASSET_DIR: ${{ matrix.asset_name }}
222228
run: |
223229
# Ensure PyInstaller can find the modules (use correct path separator for OS)
224230
SEP=$(python -c "import os; print(os.pathsep)")
225231
export PYTHONPATH="${PWD}/src${SEP}${PYTHONPATH:-}"
226232
echo "PYTHONPATH: $PYTHONPATH"
227233
228234
# Run PyInstaller with explicit paths
229-
python -m PyInstaller --clean -y --distpath "./dist/${{ matrix.asset_name }}" treemapper.spec
235+
python -m PyInstaller --clean -y --distpath "./dist/${ASSET_DIR}" treemapper.spec
230236
231237
# Verify the built executable exists (cross-platform)
232-
echo "Checking for built executable in: ./dist/${{ matrix.asset_name }}/"
233-
ls -la "./dist/${{ matrix.asset_name }}/" || echo "ERROR: dist directory not found!"
238+
echo "Checking for built executable in: ./dist/${ASSET_DIR}/"
239+
ls -la "./dist/${ASSET_DIR}/" || echo "ERROR: dist directory not found!"
234240
235241
- name: Determine architecture
236242
id: arch
237243
shell: bash
244+
env:
245+
RUNNER_PLATFORM: ${{ runner.os }}
246+
RUNNER_ARCHITECTURE: ${{ runner.arch }}
238247
run: |
239248
ARCH=$(uname -m)
240-
if [[ "${{ runner.os }}" == "Windows" ]]; then
241-
if [[ "${{ runner.arch }}" == "X64" ]]; then ARCH="x86_64"; \
242-
elif [[ "${{ runner.arch }}" == "ARM64" ]]; then ARCH="arm64"; \
249+
if [[ "$RUNNER_PLATFORM" == "Windows" ]]; then
250+
if [[ "$RUNNER_ARCHITECTURE" == "X64" ]]; then ARCH="x86_64"; \
251+
elif [[ "$RUNNER_ARCHITECTURE" == "ARM64" ]]; then ARCH="arm64"; \
243252
else ARCH="unknown"; fi
244-
elif [[ "${{ runner.os }}" == "macOS" ]] && [[ "$ARCH" == "arm64" ]]; then
253+
elif [[ "$RUNNER_PLATFORM" == "macOS" ]] && [[ "$ARCH" == "arm64" ]]; then
245254
echo "Detected ARM on macOS"
246255
fi
247256
echo "Determined ARCH: $ARCH"
@@ -250,14 +259,18 @@ jobs:
250259
- name: Rename asset with proper name
251260
shell: bash
252261
working-directory: ./repo
262+
env:
263+
ASSET_DIR: ${{ matrix.asset_name }}
264+
ARCH: ${{ steps.arch.outputs.arch }}
265+
VERSION: ${{ needs.prepare-version.outputs.version }}
266+
RUNNER_PLATFORM: ${{ runner.os }}
253267
run: |
254-
ASSET_NAME="treemapper-${{ matrix.asset_name }}-${{ steps.arch.outputs.arch }}"
255-
ASSET_NAME="${ASSET_NAME}-${{ needs.prepare-version.outputs.version }}"
256-
if [[ "${{ runner.os }}" == "Windows" ]]; then
268+
ASSET_NAME="treemapper-${ASSET_DIR}-${ARCH}-${VERSION}"
269+
if [[ "$RUNNER_PLATFORM" == "Windows" ]]; then
257270
ASSET_NAME="${ASSET_NAME}.exe"
258-
mv "./dist/${{ matrix.asset_name }}/treemapper.exe" "./dist/${ASSET_NAME}"
271+
mv "./dist/${ASSET_DIR}/treemapper.exe" "./dist/${ASSET_NAME}"
259272
else
260-
mv "./dist/${{ matrix.asset_name }}/treemapper" "./dist/${ASSET_NAME}"
273+
mv "./dist/${ASSET_DIR}/treemapper" "./dist/${ASSET_NAME}"
261274
fi
262275
263276
- name: Upload artifact
@@ -285,10 +298,12 @@ jobs:
285298

286299
- name: Restore repository from bundle
287300
shell: bash
301+
env:
302+
TAG_NAME: ${{ needs.prepare-version.outputs.tag_name }}
288303
run: |
289304
git clone repo.bundle repo
290305
cd repo
291-
git checkout ${{ needs.prepare-version.outputs.tag_name }}
306+
git checkout "$TAG_NAME"
292307
293308
# Debug: verify we're in the right state
294309
echo "Current directory: $(pwd)"
@@ -306,27 +321,34 @@ jobs:
306321

307322
- name: Install build tools
308323
working-directory: ./repo
324+
env:
325+
EXPECTED_VERSION: ${{ needs.prepare-version.outputs.version }}
309326
run: |
310327
python -m pip install --upgrade pip
311328
pip install build
312329
313330
# Verify version.py has the correct version
314331
echo "Version in version.py:"
315332
cat src/treemapper/version.py
316-
echo "Expected version: ${{ needs.prepare-version.outputs.version }}"
333+
echo "Expected version: ${EXPECTED_VERSION}"
317334
318335
- name: Build sdist and wheel
319336
working-directory: ./repo
337+
env:
338+
VERSION: ${{ needs.prepare-version.outputs.version }}
320339
run: |
321340
# Build the distribution packages
322341
python -m build
323342
324343
# Verify the built packages have correct version in filename
325344
echo "Built packages:"
326345
ls -la dist/
327-
VERSION="${{ needs.prepare-version.outputs.version }}"
346+
if [ -z "$VERSION" ]; then
347+
echo "ERROR: VERSION is empty"
348+
exit 1
349+
fi
328350
echo "Looking for version ${VERSION} in package names..."
329-
ls dist/*${VERSION}* || echo "WARNING: No packages found!"
351+
ls dist/*"${VERSION}"* || echo "WARNING: No packages found!"
330352
331353
- name: Publish package distributions to PyPI
332354
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1
@@ -354,19 +376,23 @@ jobs:
354376
- name: Restore repository from bundle
355377
env:
356378
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
379+
COMMIT_SHA: ${{ needs.prepare-version.outputs.commit_sha }}
380+
REPO_FULL_NAME: ${{ github.repository }}
357381
run: |
358382
git clone repo.bundle repo
359383
cd repo
360384
# Checkout the version commit (not old main) to preserve version bump
361-
git checkout ${{ needs.prepare-version.outputs.commit_sha }}
385+
git checkout "$COMMIT_SHA"
362386
echo "Current commit: $(git rev-parse HEAD)"
363-
echo "Expected commit: ${{ needs.prepare-version.outputs.commit_sha }}"
387+
echo "Expected commit: ${COMMIT_SHA}"
364388
365389
# Set up remote to point to the actual GitHub repository
366-
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git"
390+
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${REPO_FULL_NAME}.git"
367391
368392
- name: Push commit and tag to main
369393
working-directory: ./repo
394+
env:
395+
TAG_NAME: ${{ needs.prepare-version.outputs.tag_name }}
370396
run: |
371397
git config user.name "github-actions[bot]"
372398
# 41898282 is GitHub's bot user ID for github-actions[bot]
@@ -391,7 +417,7 @@ jobs:
391417
git push origin HEAD:main
392418
393419
# Push the tag
394-
git push origin ${{ needs.prepare-version.outputs.tag_name }}
420+
git push origin "$TAG_NAME"
395421
396422
echo "Successfully pushed version commit and tag"
397423

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# pyproject.toml
22

33
[build-system]
4-
requires = ["setuptools>=45", "wheel"]
4+
requires = ["setuptools>=77", "wheel"]
55
build-backend = "setuptools.build_meta"
66

77
[tool.black]

src/treemapper/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "1.3.1" # This will be replaced during the build process
1+
__version__ = "1.3.1"

0 commit comments

Comments
 (0)