-
Notifications
You must be signed in to change notification settings - Fork 7
ci(release): switch to fully manual releases, tagging, and changelog #112
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
base: master
Are you sure you want to change the base?
Changes from all commits
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,57 +1,35 @@ | ||
| name: Release | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| push: | ||
| tags: | ||
| - "v*" | ||
|
|
||
| jobs: | ||
| release: | ||
| name: Semantic Release | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
| id-token: write | ||
|
|
||
| outputs: | ||
| released: ${{ steps.release.outputs.released }} | ||
| tag: ${{ steps.release.outputs.tag }} | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| token: ${{ secrets.RELEASE_TOKEN }} | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: "3.13" | ||
|
|
||
| - name: Python Semantic Release | ||
| id: release | ||
| uses: python-semantic-release/python-semantic-release@v9 | ||
| with: | ||
| github_token: ${{ secrets.RELEASE_TOKEN }} | ||
|
|
||
| publish: | ||
| name: Publish to PyPI | ||
| needs: release | ||
| if: needs.release.outputs.released == 'true' | ||
| runs-on: ubuntu-latest | ||
| environment: pypi | ||
| permissions: | ||
| id-token: write | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
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. 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: sed -n '1,120p' .github/workflows/release.yml
printf '\n--- checkout usage ---\n'
rg -n -C 3 'actions/checkout|persist-credentials|git (push|fetch)|gh |git credential|GITHUB_TOKEN' .github/workflows .github 2>/dev/null || trueRepository: krishnamodepalli/django-sysconfig Length of output: 4109 🌐 Web query:
💡 Result: In actions/checkout v4, the persist-credentials input controls whether the action configures the authentication token or SSH key in the local Git configuration [1][2]. Key details include: Default Behavior: The default value for persist-credentials is true [2][3]. Mechanism: When set to true, the action configures the authentication credentials (either the GITHUB_TOKEN or an SSH key) into the local Git configuration [1][4]. This allows subsequent Git commands executed in the same job to run using the same authenticated credentials [5][6]. Cleanup: The action automatically removes these credentials during the post-job cleanup phase [1][4]. Security and Opt-out: You can disable this behavior by setting persist-credentials: false [1][5]. This is recommended in scenarios where you do not need to run subsequent authenticated Git commands, as it minimizes the exposure of credentials within the job environment [7]. Important Version Distinction: While actions/checkout v4 persists credentials directly into the local.git/config [7], newer versions (starting from v6) have improved security by storing credentials in a separate file under $RUNNER_TEMP and using a Git configuration include to reference them [7][8]. If you are using v4, be aware that the credentials are written to the local Git configuration [7]. Citations:
🌐 Web query:
💡 Result: When you specify the permissions key in a GitHub Actions workflow or job, any permission not explicitly listed is automatically set to none [1][2]. The actions/checkout action requires the contents: read permission to clone private repositories using the default GITHUB_TOKEN [3][4][5]. If this permission is omitted or set to none while attempting to access a private repository, the checkout process will fail [3][6]. For public repositories, the actions/checkout action can perform an anonymous clone, meaning it may function without any permissions (permissions: {}) [7]. However, for private repositories, or when you wish to ensure consistent behavior, it is officially recommended to explicitly set the following permission [3][4][5]: permissions: contents: read If you use the permissions key, the GITHUB_TOKEN's access is restricted to exactly what you define [1][8]. Omitting a scope effectively removes all access for that scope, which is a core feature of the principle of least privilege in GitHub Actions [9][10][11]. Citations:
Disable persisted checkout credentials. Set 🧰 Tools🪛 zizmor (1.29.0)[warning] 17-17: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false (artipacked) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
| with: | ||
| ref: ${{ needs.release.outputs.tag }} | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: "3.13" | ||
|
|
||
| - name: Verify tag matches pyproject.toml version | ||
| run: | | ||
| TAG_VERSION="${GITHUB_REF_NAME#v}" | ||
| PROJECT_VERSION="$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')" | ||
| if [ "$TAG_VERSION" != "$PROJECT_VERSION" ]; then | ||
| echo "Tag $GITHUB_REF_NAME does not match pyproject.toml version $PROJECT_VERSION" >&2 | ||
| exit 1 | ||
| fi | ||
|
Comment on lines
+24
to
+31
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. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n '(^version\s*=|__version__|importlib\.metadata)' \
pyproject.toml django_sysconfig/__init__.pyRepository: krishnamodepalli/django-sysconfig Length of output: 260 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- version references ---'
rg -n --hidden -g '!*.lock' -g '!dist/**' -g '!build/**' \
'django_sysconfig\.__version__|from django_sysconfig import __version__|importlib\.metadata|metadata\.version|project\.version|__version__' .
printf '%s\n' '--- package metadata configuration ---'
sed -n '1,80p' pyproject.toml
sed -n '1,80p' django_sysconfig/__init__.py
printf '%s\n' '--- contributor release instructions ---'
rg -n -A8 -B4 'version|release|tag' CONTRIBUTING.mdRepository: krishnamodepalli/django-sysconfig Length of output: 5127 🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
import re
import tomllib
from pathlib import Path
project = tomllib.loads(Path("pyproject.toml").read_bytes().decode())["project"]["version"]
init_text = Path("django_sysconfig/__init__.py").read_text()
match = re.search(r'(?m)^__version__\s*=\s*["\']([^"\']+)["\']\s*$', init_text)
if match is None:
raise SystemExit("django_sysconfig.__version__ is not defined")
module = match.group(1)
print(f"pyproject.toml [project].version: {project}")
print(f"django_sysconfig.__version__: {module}")
print(f"values_equal: {project == module}")
PYRepository: krishnamodepalli/django-sysconfig Length of output: 291 🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
import re
from pathlib import Path
pyproject = Path("pyproject.toml").read_text()
init = Path("django_sysconfig/__init__.py").read_text()
project_match = re.search(r'(?m)^\s*version\s*=\s*["\']([^"\']+)["\']\s*$', pyproject)
module_match = re.search(r'(?m)^\s*__version__\s*=\s*["\']([^"\']+)["\']\s*$', init)
if project_match is None:
raise SystemExit("pyproject.toml [project].version is not defined")
if module_match is None:
raise SystemExit("django_sysconfig.__version__ is not defined")
project = project_match.group(1)
module = module_match.group(1)
print(f"pyproject.toml [project].version: {project}")
print(f"django_sysconfig.__version__: {module}")
print(f"values_equal: {project == module}")
PYRepository: krishnamodepalli/django-sysconfig Length of output: 266 Keep the package version values synchronized.
🤖 Prompt for AI Agents |
||
|
|
||
| - name: Build package | ||
| run: | | ||
| pip install build | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -70,6 +70,18 @@ DJANGO_SETTINGS_MODULE=settings_dev django-admin runserver | |||||||||||||||
|
|
||||||||||||||||
| --- | ||||||||||||||||
|
|
||||||||||||||||
| ## Release process (maintainers) | ||||||||||||||||
|
|
||||||||||||||||
| Releases are fully manual — merging to `master` never triggers a release. | ||||||||||||||||
|
|
||||||||||||||||
| 1. Bump the version in `pyproject.toml` (`project.version`) and `django_sysconfig/__init__.py` (`__version__`) | ||||||||||||||||
| 2. Move the `## [Unreleased]` entries in `CHANGELOG.md` under a new `## [X.Y.Z] - YYYY-MM-DD` heading, grouped into `Added`/`Changed`/`Deprecated`/`Removed`/`Fixed`/`Security` per [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) | ||||||||||||||||
|
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. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Document the commit-to-changelog rules. Step 2 defines headings but omits these required rules:
Add these rules so maintainers apply the same categorization during manual releases. 🤖 Prompt for AI Agents |
||||||||||||||||
| 3. Commit: `chore(release): X.Y.Z` | ||||||||||||||||
| 4. Tag: `git tag vX.Y.Z` and `git push origin vX.Y.Z` | ||||||||||||||||
| 5. The tag push triggers the `Release` workflow, which builds and publishes to PyPI | ||||||||||||||||
|
Comment on lines
+79
to
+81
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. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Push the release commit to Step 3 creates the release commit locally. Step 4 pushes only Proposed fix-4. Tag: `git tag vX.Y.Z` and `git push origin vX.Y.Z`
-5. The tag push triggers the `Release` workflow, which builds and publishes to PyPI
+4. Tag: `git tag vX.Y.Z`
+5. Push the release commit and tag: `git push origin HEAD:master vX.Y.Z`
+6. The tag push triggers the `Release` workflow, which builds and publishes to PyPI📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||
|
|
||||||||||||||||
| --- | ||||||||||||||||
|
|
||||||||||||||||
| ## Reporting bugs | ||||||||||||||||
|
|
||||||||||||||||
| Open an issue on [GitHub Issues](https://github.com/krishnamodepalli/django-sysconfig/issues) with: | ||||||||||||||||
|
|
||||||||||||||||
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.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: krishnamodepalli/django-sysconfig
Length of output: 4183
🌐 Web query:
GitHub Actions workflow permissions unspecified permissions set to none contents checkout required contents read actions/checkout official documentation💡 Result:
In GitHub Actions, if you include a permissions key in your workflow or job configuration, all permissions not explicitly defined are automatically set to none [1][2][3]. This is a critical security mechanism that overrides any default repository or organization-level permission settings [4]. Because the actions/checkout action requires permission to read the repository's contents to fetch code, you must explicitly grant it the necessary access if you define a permissions block. Specifically, you must include contents: read within your permissions configuration [5][6][7][4]. Example of the required configuration: permissions: contents: read If you fail to include contents: read while defining other permissions, the actions/checkout step will fail because it will default to none, effectively revoking its access to the repository [4]. Note that if your workflow requires additional operations—such as pushing code back to the repository—you would instead need to set contents: write [8].
Citations:
Add
contents: readto the job permissions.The job grants only
id-token: write, socontentsdefaults tonone.actions/checkout@v4requirescontents: readand can fail without it.🤖 Prompt for AI Agents