Skip to content

Commit 208ef86

Browse files
committed
Add shared dev-workspace setup script and harden CI workflow
Extract venv creation/validation into scripts/setup-dev-workspace.sh so CI and the devcontainer install the same editable local sources instead of drifting apart. Update the CI workflow to use it, add per-branch concurrency cancellation, cache all monorepo venvs, run inspect_code/ audit_code/check_formatting steps, support Windows venv paths, and bump upload-artifact to v5.
1 parent fd7e302 commit 208ef86

2 files changed

Lines changed: 110 additions & 14 deletions

File tree

.github/workflows/ci-cd.yml

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# TODO: check formatting
21
# TODO: lock files on all platforms
32
# TODO: test with all supported python versions
43
name: CI
@@ -15,6 +14,14 @@ on:
1514
type: boolean
1615
default: false
1716

17+
# Cancel superseded runs only for PR pushes (rapid commits to the same branch).
18+
# Never cancel push/tag/merge_group runs — a tag push drives the PyPI publish
19+
# steps below and a merge_group run backs a required check in the merge queue;
20+
# either could be silently aborted by an unrelated event sharing the ref.
21+
concurrency:
22+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
23+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
24+
1825
defaults:
1926
run:
2027
shell: bash
@@ -27,18 +34,21 @@ env:
2734
jobs:
2835
build:
2936
runs-on: ${{ matrix.os }}
37+
timeout-minutes: 40
3038
strategy:
3139
fail-fast: false
32-
max-parallel: 1
3340
matrix:
3441
os: [ubuntu-24.04, macos-15, windows-2022]
3542
include:
3643
- os: ubuntu-24.04
3744
name: Linux
45+
venv_bin: bin
3846
- os: macos-15
3947
name: macOS
48+
venv_bin: bin
4049
- os: windows-2022
4150
name: Windows
51+
venv_bin: Scripts
4252

4353
steps:
4454
- uses: actions/checkout@v5
@@ -53,32 +63,62 @@ jobs:
5363
with:
5464
python-version: ${{ env.DEV_WORKSPACE_PYTHON_VERSION }}
5565

66+
# Every package in the monorepo has its own .venvs/ (root dev_workspace plus one
67+
# per project for envs created by `prepare-envs`, e.g. dev_no_runtime, runtime).
68+
# Exact-match only (no restore-keys): setup-dev-workspace.sh and prepare-envs
69+
# skip reinstalling whenever a restored venv already looks valid, so a stale
70+
# partial-match restore could mask a dependency that was added since the cache
71+
# was written.
72+
- name: Cache all venvs
73+
uses: actions/cache@v4
74+
with:
75+
path: |
76+
.venvs
77+
**/.venvs
78+
key: ${{ runner.os }}-venvs-${{ hashFiles('**/pyproject.toml', '**/preset.toml') }}
79+
5680
- name: Install dependencies
5781
run: |
58-
pipx run finecode bootstrap
59-
source .venvs/dev_workspace/bin/activate
82+
# CI must exercise this branch's local source, so finecode and its sibling
83+
# packages need an editable install, not a released version from PyPI — see
84+
# docs/guides/developing-finecode.md#continuous-integration.
85+
sh scripts/setup-dev-workspace.sh
86+
source .venvs/dev_workspace/${{ matrix.venv_bin }}/activate
87+
shell: bash
88+
89+
- name: Inspect code
90+
if: ${{ !cancelled() }}
91+
run: |
92+
source .venvs/dev_workspace/${{ matrix.venv_bin }}/activate
93+
python -m finecode run inspect_code
94+
shell: bash
6095

61-
python -m finecode prepare-envs
96+
- name: Audit code
97+
if: ${{ !cancelled() }}
98+
run: |
99+
source .venvs/dev_workspace/${{ matrix.venv_bin }}/activate
100+
python -m finecode run audit_code
62101
shell: bash
63102

64-
- name: Lint
103+
- name: Check formatting
104+
if: ${{ !cancelled() }}
65105
run: |
66-
source .venvs/dev_workspace/bin/activate
67-
python -m finecode run lint
106+
source .venvs/dev_workspace/${{ matrix.venv_bin }}/activate
107+
python -m finecode run check_formatting
68108
shell: bash
69109

70110
- name: Build artifacts
71111
id: build
72-
if: runner.os == 'Linux'
112+
if: runner.os == 'Linux' && !cancelled()
73113
run: |
74-
source .venvs/dev_workspace/bin/activate
114+
source .venvs/dev_workspace/${{ matrix.venv_bin }}/activate
75115
python -m finecode run build_artifact
76116
shell: bash
77117

78118
- name: Run unit tests
79119
if: ${{ !cancelled() }}
80120
run: |
81-
source .venvs/dev_workspace/bin/activate
121+
source .venvs/dev_workspace/${{ matrix.venv_bin }}/activate
82122
# TODO: test with all supported python versions
83123
python -m finecode run run_tests
84124
shell: bash
@@ -89,7 +129,7 @@ jobs:
89129
FINECODE_CONFIG_PUBLISH_AND_VERIFY_ARTIFACT__INIT_REPOSITORY_PROVIDER__REPOSITORIES: '[{"name": "testpypi", "url": "https://test.pypi.org/"}]'
90130
FINECODE_CONFIG_PUBLISH_AND_VERIFY_ARTIFACT__INIT_REPOSITORY_PROVIDER__CREDENTIALS_BY_REPOSITORY: '{"testpypi": {"username": "${{ secrets.TESTPYPI_USERNAME }}", "password": "${{ secrets.TESTPYPI_PASSWORD }}"}}'
91131
run: |
92-
source .venvs/dev_workspace/bin/activate
132+
source .venvs/dev_workspace/${{ matrix.venv_bin }}/activate
93133
python -m finecode run \
94134
--map-payload-fields="src-artifact-def-path,dist-artifact-paths" \
95135
publish_and_verify_artifact \
@@ -104,7 +144,7 @@ jobs:
104144
FINECODE_CONFIG_PUBLISH_AND_VERIFY_ARTIFACT__INIT_REPOSITORY_PROVIDER__CREDENTIALS_BY_REPOSITORY: '{"pypi": {"username": "${{ secrets.PYPI_USERNAME }}", "password": "${{ secrets.PYPI_PASSWORD }}"}}'
105145
run: |
106146
# TODO: make sure git tag exists (for manual trigger)
107-
source .venvs/dev_workspace/bin/activate
147+
source .venvs/dev_workspace/${{ matrix.venv_bin }}/activate
108148
python -m finecode run \
109149
--map-payload-fields="src-artifact-def-path,dist-artifact-paths" \
110150
publish_and_verify_artifact \
@@ -115,7 +155,7 @@ jobs:
115155

116156
# TODO: try to replace by finecode action
117157
- name: Store the distribution packages
118-
uses: actions/upload-artifact@v4
158+
uses: actions/upload-artifact@v5
119159
if: runner.os == 'Linux'
120160
with:
121161
name: python-package-distributions

scripts/setup-dev-workspace.sh

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/bin/sh
2+
# Creates (or validates) the dev_workspace venv with all monorepo packages installed
3+
# editable from local source. Shared by the devcontainer and CI so the two setup paths
4+
# cannot drift apart — see docs/guides/developing-finecode.md#continuous-integration.
5+
#
6+
# Must be run from the repo root.
7+
set -eu
8+
9+
VENV_DIR=".venvs/dev_workspace"
10+
11+
# venv layout differs on Windows (python.exe under Scripts/, not bin/); this script also
12+
# runs under Git Bash on GitHub Actions' windows-* runners, where `uname -s` reports
13+
# MINGW/MSYS/CYGWIN rather than a POSIX name.
14+
case "$(uname -s)" in
15+
MINGW* | MSYS* | CYGWIN*) VENV_PYTHON="$VENV_DIR/Scripts/python.exe" ;;
16+
*) VENV_PYTHON="$VENV_DIR/bin/python" ;;
17+
esac
18+
19+
is_valid_venv() {
20+
if [ ! -x "$VENV_PYTHON" ]; then
21+
return 1
22+
fi
23+
24+
# Requested check first; fall back to a guaranteed CLI entrypoint check.
25+
if "$VENV_PYTHON" -m finecode version >/dev/null 2>&1; then
26+
return 0
27+
fi
28+
29+
if "$VENV_PYTHON" -m finecode version >/dev/null 2>&1; then
30+
return 0
31+
fi
32+
33+
"$VENV_PYTHON" -m finecode --help >/dev/null 2>&1
34+
}
35+
36+
recreate_venv() {
37+
rm -rf "$VENV_DIR"
38+
python -m venv "$VENV_DIR"
39+
40+
# Ensure expected uv version is present in pipx-managed tools.
41+
pipx install --force "uv==0.11.*"
42+
43+
# bootstrap cannot be used here because finecode has to be installed from local sources.
44+
# The editable package list is generated (not hand-maintained) so it can't go stale —
45+
# see list_dev_workspace_editables.py and docs/guides/developing-finecode.md#continuous-integration.
46+
EDITABLE_ARGS=$(python scripts/list_dev_workspace_editables.py)
47+
uv pip install --python "$VENV_PYTHON" --group dev_workspace $EDITABLE_ARGS -e .
48+
49+
"$VENV_PYTHON" -m finecode prepare-envs
50+
}
51+
52+
if [ -d "$VENV_DIR" ] && is_valid_venv; then
53+
echo "dev_workspace venv is valid; skipping recreation."
54+
else
55+
recreate_venv
56+
fi

0 commit comments

Comments
 (0)