Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
name: Python CI

on:
push:
branches: [main]
pull_request:
branches:
- "**"
# This is so we can call CI locally from other workflows that might want to
# run CI before doing whatever task they're doing. Like the release workflow.
workflow_call:

defaults:
run:
Expand Down
90 changes: 90 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Python CI
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow name is "Python CI" which is the same as the ci.yml workflow. This creates ambiguity in the GitHub Actions UI where both workflows will appear with identical names. Consider renaming this to something more specific like "Release" or "Python Release" to clearly distinguish it from the CI workflow.

Suggested change
name: Python CI
name: Python Release

Copilot uses AI. Check for mistakes.

on:
push:
branches: [main]

jobs:
run_tests:
uses: ./.github/workflows/ci.yml

release:
needs: run_tests
runs-on: ubuntu-latest
if: github.ref_name == 'main'
concurrency:
group: ${{ github.workflow }}-release-${{ github.ref_name }}
cancel-in-progress: false

permissions:
contents: write

steps:
# Note: We checkout the repository at the branch that triggered the workflow.
# Python Semantic Release will automatically convert shallow clones to full clones
# if needed to ensure proper history evaluation. However, we forcefully reset the
# branch to the workflow sha because it is possible that the branch was updated
# while the workflow was running, which prevents accidentally releasing un-evaluated
# changes.
- name: Setup | Checkout Repository on Release Branch
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}

- name: Setup | Force release branch to be at workflow sha
run: |
git reset --hard ${{ github.sha }}
- name: Action | Semantic Version Release
id: release
# Adjust tag with desired version if applicable.
uses: python-semantic-release/[email protected]
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
git_committer_name: "github-actions"
git_committer_email: "[email protected]"
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The release workflow doesn't specify a working-directory for the semantic release action, but the pyproject.toml file is located in the backend/ directory. Python-semantic-release needs to run in the backend/ directory where pyproject.toml is located. Either add a 'root_options' parameter with 'path: backend' to the semantic release action, or set the working-directory in the workflow defaults or step level.

Suggested change
git_committer_email: "[email protected]"
git_committer_email: "[email protected]"
root_options: |
path: backend

Copilot uses AI. Check for mistakes.

- name: Publish | Upload to GitHub Release Assets
uses: python-semantic-release/[email protected]
if: steps.release.outputs.released == 'true'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ steps.release.outputs.tag }}

- name: Upload | Distribution Artifacts
uses: actions/upload-artifact@v4
with:
name: distribution-artifacts
path: dist
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The artifact upload path is set to 'dist', but the distribution artifacts will be created in backend/dist (since the pyproject.toml is in the backend directory). This should be 'backend/dist' to correctly upload the build artifacts.

Copilot uses AI. Check for mistakes.
if-no-files-found: error

outputs:
released: ${{ steps.release.outputs.released || 'false' }}

deploy:
# 1. Separate out the deploy step from the publish step to run each step at
# the least amount of token privilege
# 2. Also, deployments can fail, and its better to have a separate job if you need to retry
# and it won't require reversing the release.
runs-on: ubuntu-latest
needs: release
if: github.ref_name == 'main' && needs.release.outputs.released == 'true'

permissions:
contents: read
id-token: write

steps:
- name: Setup | Download Build Artifacts
uses: actions/download-artifact@v4
id: artifact-download
with:
name: distribution-artifacts
path: dist

- name: Publish to PyPi
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The packages-dir parameter is set to 'dist', but since python-semantic-release runs in the root directory (not backend/) and the pyproject.toml is in backend/, the built distribution artifacts will likely be in backend/dist. This path should be 'backend/dist' to match where the artifacts are actually created.

Copilot uses AI. Check for mistakes.
user: __token__
password: ${{ secrets.PYPI_UPLOAD_TOKEN }}
Comment on lines +89 to +90
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pypa/gh-action-pypi-publish action is being used with deprecated parameters. The 'user' and 'password' parameters are deprecated in favor of trusted publishing. Since the job already has 'id-token: write' permission configured (line 75), which is required for trusted publishing, the 'user' and 'password' parameters should be removed to use the more secure OIDC-based trusted publisher flow instead of API tokens.

Suggested change
user: __token__
password: ${{ secrets.PYPI_UPLOAD_TOKEN }}

Copilot uses AI. Check for mistakes.
19 changes: 2 additions & 17 deletions backend/docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,13 @@
from subprocess import check_call

from django import setup as django_setup


def get_version(*file_paths):
"""
Extract the version string from the file.

Input:
- file_paths: relative path fragments to file with
version string
"""
filename = os.path.join(os.path.dirname(__file__), *file_paths)
version_file = open(filename, encoding="utf8").read()
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M)
if version_match:
return version_match.group(1)
raise RuntimeError('Unable to find version string.')
from importlib.metadata import version as get_version


REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(REPO_ROOT)

VERSION = get_version('../sample_plugin', '__init__.py')
VERSION = get_version('openedx-sample-plugin')
# Configure Django for autodoc usage
os.environ['DJANGO_SETTINGS_MODULE'] = 'test_settings'
django_setup()
Expand Down
8 changes: 6 additions & 2 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ name = "openedx-sample-plugin"
description = "A sample backend plugin for the Open edX Platform"
requires-python = ">=3.11"
license="Apache-2.0"
version = "0.1.0"
authors = [
{name = "Open edX Project", email = "[email protected]"},
]
Expand All @@ -24,7 +25,7 @@ keywords= [
"edx",
]

dynamic = ["version", "readme", "dependencies"]
dynamic = ["readme", "dependencies"]

[project.entry-points."lms.djangoapp"]
sample_plugin = "sample_plugin.apps:SamplePluginConfig"
Expand All @@ -37,10 +38,13 @@ Homepage = "https://openedx.org/openedx/sample-plugin"
Repository = "https://openedx.org/openedx/sample-plugin"

[tool.setuptools.dynamic]
version = {attr = "sample_plugin.__version__"}
readme = {file = ["README.rst", "CHANGELOG.rst"]}
dependencies = {file = "requirements/base.in"}

[tool.setuptools.packages.find]
include = ["sample_plugin*"]
exclude = ["sample_plugin.tests*"]

[tool.semantic_release.changelog.default_templates]
changelog_file = "CHANGELOG.rst"
output_format = "rst"
Comment on lines +48 to +50
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The semantic release configuration appears incomplete. The 'changelog.default_templates' section shown here is not valid configuration syntax for python-semantic-release v8+. The correct configuration should use sections like [tool.semantic_release] with proper options such as version_toml, branch, changelog_file, and build_command. The current configuration with 'changelog.default_templates' and 'output_format' is not recognized by the tool and may cause the release process to fail.

Suggested change
[tool.semantic_release.changelog.default_templates]
changelog_file = "CHANGELOG.rst"
output_format = "rst"
[tool.semantic_release]
version_toml = "pyproject.toml:project.version"
changelog_file = "CHANGELOG.rst"

Copilot uses AI. Check for mistakes.
6 changes: 5 additions & 1 deletion backend/sample_plugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@
A sample backend plugin for the Open edX Platform.
"""

__version__ = "0.1.0"
from importlib.metadata import version as get_version

# The name of the package is `opnedx-sample-plugin` but __package__ is `sample_plugin` so we hardcode the name of the
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a typo in the comment. The package name is written as "opnedx-sample-plugin" but should be "openedx-sample-plugin" (missing the 'e' in 'openedx'). This inconsistency could be confusing to developers reading this code.

Suggested change
# The name of the package is `opnedx-sample-plugin` but __package__ is `sample_plugin` so we hardcode the name of the
# The name of the package is `openedx-sample-plugin` but __package__ is `sample_plugin` so we hardcode the name of the

Copilot uses AI. Check for mistakes.
# package here so that the version fetching works correctly. A lot of examples will show using `__package__`.
__version__ = get_version('openedx-sample-plugin')