-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Add CI and CD workflows for automated build, testing, and release #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c123a12
75f2c6c
7d24be7
c72c099
a9d847c
cc9364a
a3d5d98
056c6a4
50c5f50
48dd2ed
6d984aa
491fd8a
a8e42e4
096f739
b7f65e6
b886a34
b0cae5c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,29 +1,3 @@ | ||
| [flake8] | ||
| max-line-length = 100 | ||
| extend-ignore = | ||
| # whitespace before ':' (conflicts with black) | ||
| E203, | ||
| # line break before binary operator (conflicts with black) | ||
| W503, | ||
| # assert statements in tests | ||
| S101, | ||
| # subprocess calls with shell=False are safe (we explicitly use list args) | ||
| S603, | ||
| # start process with partial path (uv, git calls) | ||
| S607, | ||
| # hardcoded SQL string detection (handled by SENTINEL rules, not flake8) | ||
| S608 | ||
|
|
||
| per-file-ignores = | ||
| tests/*: S101, S106, S105 | ||
|
|
||
| exclude = | ||
| .git, | ||
| __pycache__, | ||
| .venv, | ||
| venv, | ||
| build, | ||
| dist, | ||
| *.egg-info, | ||
| .mypy_cache, | ||
| .ruff_cache | ||
| extend-ignore = E501 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| name: SENTINEL CD | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| push: | ||
| branches: | ||
| - main | ||
|
|
||
| jobs: | ||
| # ── 1. Build ───────────────────────────────────────────────────────────────── | ||
| build: | ||
| name: Build wheel & sdist | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| version: ${{ steps.ver.outputs.version }} | ||
| version_changed: ${{ steps.ver.outputs.changed }} | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Check if VERSION changed | ||
| id: ver | ||
| run: | | ||
| NULL_SHA="0000000000000000000000000000000000000000" | ||
| if [ "${{ github.event_name }}" = "push" ] && [ "${{ github.event.before }}" != "${NULL_SHA}" ]; then | ||
| RANGE="${{ github.event.before }}..${{ github.sha }}" | ||
| else | ||
| RANGE="HEAD~1..HEAD" | ||
| fi | ||
| if git diff --quiet "${RANGE}" -- VERSION; then | ||
| echo "changed=false" >> $GITHUB_OUTPUT | ||
| echo "version=$(cat VERSION | tr -d '[:space:]')" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "changed=true" >> $GITHUB_OUTPUT | ||
| echo "version=$(cat VERSION | tr -d '[:space:]')" >> $GITHUB_OUTPUT | ||
| fi | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: "3.11" | ||
|
|
||
| - name: Install uv | ||
| uses: astral-sh/setup-uv@v4 | ||
|
|
||
| - name: Ensure static manifests are version-synced | ||
| shell: bash | ||
| run: | | ||
| python scripts/sync_version.py | ||
| if [ -n "$(git status --porcelain)" ]; then | ||
| echo "::error::Static manifests are out of sync with VERSION. Run: python scripts/sync_version.py and commit the updates." | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Build package | ||
| run: uv build | ||
|
|
||
| - name: Upload dist artifacts | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: dist | ||
| path: dist/ | ||
| retention-days: 7 | ||
|
|
||
| # ── 2. Tag & GitHub Release ────────────────────────────────────────────────── | ||
| release: | ||
| name: Tag & GitHub Release | ||
| needs: build | ||
| if: needs.build.outputs.version_changed == 'true' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Download dist artifacts | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: dist | ||
| path: dist/ | ||
|
|
||
| - name: Create and push tag | ||
| id: tag | ||
| run: | | ||
| TAG="v${{ needs.build.outputs.version }}" | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "github-actions[bot]@users.noreply.github.com" | ||
| if git ls-remote --tags origin | grep -q "refs/tags/${TAG}$"; then | ||
| echo "Tag ${TAG} already exists — skipping." | ||
| echo "created=false" >> $GITHUB_OUTPUT | ||
| else | ||
| git tag -a "${TAG}" -m "Release ${TAG}" | ||
| git push origin "${TAG}" | ||
| echo "created=true" >> $GITHUB_OUTPUT | ||
| fi | ||
| echo "tag=${TAG}" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: Create GitHub Release | ||
| if: steps.tag.outputs.created == 'true' | ||
| uses: softprops/action-gh-release@v2 | ||
| with: | ||
| tag_name: ${{ steps.tag.outputs.tag }} | ||
| name: SENTINEL ${{ steps.tag.outputs.tag }} | ||
| generate_release_notes: true | ||
| files: dist/* | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| name: SENTINEL CI | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| pull_request: | ||
| branches: | ||
| - main | ||
|
|
||
| jobs: | ||
| ci: | ||
| name: Lint, Type-check & Test | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Ensure VERSION was bumped | ||
| if: github.event_name == 'pull_request' | ||
| shell: bash | ||
| run: | | ||
| SEMVER_REGEX='^[0-9]+\.[0-9]+\.[0-9]+([+-].+)?$' | ||
| BASE_VERSION="$(git show "${{ github.event.pull_request.base.sha }}:VERSION" | tr -d '[:space:]')" | ||
| HEAD_VERSION="$(git show "${{ github.event.pull_request.head.sha }}:VERSION" | tr -d '[:space:]')" | ||
| if ! [[ "$BASE_VERSION" =~ $SEMVER_REGEX ]]; then | ||
| echo "::error::Base VERSION is not valid semver: $BASE_VERSION" | ||
| exit 1 | ||
| fi | ||
| if ! [[ "$HEAD_VERSION" =~ $SEMVER_REGEX ]]; then | ||
| echo "::error::Head VERSION is not valid semver: $HEAD_VERSION" | ||
| exit 1 | ||
| fi | ||
| if [ "$BASE_VERSION" = "$HEAD_VERSION" ]; then | ||
| echo "::error::VERSION not bumped. Bump VERSION before merging." | ||
| exit 1 | ||
| fi | ||
|
Comment on lines
+20
to
+38
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot apply changes based on this feedback
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implemented in |
||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: "3.11" | ||
|
|
||
| - name: Install uv | ||
| uses: astral-sh/setup-uv@v4 | ||
|
|
||
| - name: Install dependencies | ||
| run: uv sync --group dev | ||
|
|
||
| - name: Lint (ruff) | ||
| run: uv run ruff check sentinel | ||
|
|
||
| - name: Format check (black) | ||
| run: uv run black --check sentinel | ||
|
|
||
| - name: Type-check (mypy) | ||
| run: uv run mypy sentinel | ||
|
|
||
| - name: Test | ||
| run: uv run pytest --cov=sentinel tests/ | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot apply changes based on this feedback
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented in
48dd2ed: CD now checks VERSION changes across the full push range (${{ github.event.before }}..${{ github.sha }}), with a null-SHA fallback path, so multi-commit pushes won’t miss a VERSION bump.