Skip to content
Draft
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
93 changes: 93 additions & 0 deletions .github/workflows/dependency-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: Dependency Update
Comment thread
ArBridgeman marked this conversation as resolved.
Comment thread
Rimsha2535 marked this conversation as resolved.
Copy link
Copy Markdown
Collaborator

@ArBridgeman ArBridgeman Apr 15, 2026

Choose a reason for hiding this comment

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

We also need to add documentation to:
https://exasol.github.io/python-toolbox/main/user_guide/configuration.html

To do that, we typically create a sub-page in the relevant area. In this case, we have a nice page that we can add to:
https://exasol.github.io/python-toolbox/main/user_guide/features/github_workflows/github_project_configuration.html#

Under the section Secret, we should add the Slack token name, similar to how the PYPI_TOKEN was done. Maybe then, we should also provide a link using RST (not a hyperlink) to your Dependency Update section you wrote, but we could also leave that away.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The Slack token name is INTEGRATION_TEAM_SECURITY_UPDATES_WEBHOOK,
and it's used for the SLACK_WEBHOOK_URL. We don't need to put SLACK_WEBHOOK_URL in the description, but it might be helpful to know that for what you're writing 😉

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

When we add the slack step to the dependency-update.yml, we should include Python in the title, so it's easy to search in the slack channel.


on:
schedule:
# Every Monday at 03:00 UTC
- cron: "0 3 * * 1"
workflow_dispatch:
workflow_call:

jobs:
dependency-update:
name: Dependency Update
runs-on: "ubuntu-24.04"
permissions:
contents: write
pull-requests: write

steps:
- name: Check out Repository
id: check-out-repository
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Set up Python & Poetry Environment
id: set-up-python-and-poetry-environment
uses: exasol/python-toolbox/.github/actions/python-environment@v6
with:
python-version: "3.10"
poetry-version: "2.3.0"

- name: Audit Dependencies
id: audit-dependencies
run: |
poetry run -- nox -s dependency:audit | tee vulnerabilities.json
LENGTH=$(jq 'length' vulnerabilities.json)
echo "count=$LENGTH" >> "$GITHUB_OUTPUT"

- name: Update Dependencies
id: update-dependencies
if: steps.audit-dependencies.outputs.count > 0
run: poetry update

- name: Check for poetry.lock Changes
id: check-for-poetry-lock-changes
if: steps.audit-dependencies.outputs.count > 0
run: |
if git diff --quiet -- poetry.lock; then
echo "changed=false" >> "$GITHUB_OUTPUT"
else
echo "changed=true" >> "$GITHUB_OUTPUT"
fi

- name: Configure git
id: configure-git
if: steps.check-for-poetry-lock-changes.outputs.changed == 'true'
run: |
git config --global user.email "opensource@exasol.com"
git config --global user.name "Automatic Dependency Updater"

- name: Create branch
id: create-branch
if: steps.check-for-poetry-lock-changes.outputs.changed == 'true' && github.ref == 'refs/heads/main'
run: |
branch_name="dependency-update/$(date "+%Y%m%d%H%M%S")"
echo "Creating branch $branch_name"
git checkout -b "$branch_name"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
git checkout -b "$branch_name"
git switch -C "$branch_name"

Switch is recommended instead of checkout for creating and changing branches


- name: Commit changes & push
id: publish-branch
if: steps.check-for-poetry-lock-changes.outputs.changed == 'true' && startsWith(github.ref, 'refs/heads/')
run: |
branch_name=$(git rev-parse --abbrev-ref HEAD)
git add poetry.lock
git commit --message "Update poetry.lock"
git push --set-upstream origin "$branch_name"

- name: Create pull request
id: create-pr
if: steps.check-for-poetry-lock-changes.outputs.changed == 'true' && github.ref == 'refs/heads/main'
env:
GH_TOKEN: ${{ github.token }}
run: |-
BASE_BRANCH=$(gh repo view --json defaultBranchRef -q .defaultBranchRef.name)

gh pr create \
--base "$BASE_BRANCH" \
--title "Update poetry.lock" \
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
--title "Update poetry.lock" \
--title "Update dependencies to fix vulnerabilities" \

Copy link
Copy Markdown
Contributor

@ckunki ckunki Apr 15, 2026

Choose a reason for hiding this comment

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

I assume the PRs to accumulate quite quick.
Should we assign a unique ID to the PR's title?

E.g.

  • the date, or
  • the dependencies, or vulnerabilities (probably too long and fuzzy)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I like that idea. We could include the date, as we've also got it in the branch name

--body "Automated dependency update for \`poetry.lock\`.

This PR was created by the dependency update workflow after running:
- \`poetry run -- nox -s dependency:audit\`
- \`poetry update\`"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

❤️

14 changes: 14 additions & 0 deletions doc/user_guide/features/github_workflows/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ Workflows
- Pull request and monthly
- Executes the continuous integration suite by calling ``merge-gate.yml`` and
``report.yml``. See :ref:`ci_yml` for a graph of workflow calls.
* - ``dependency-update.yml``
- Weekly and manual
- Audits project dependencies for known vulnerabilities, updates them with Poetry when needed, and creates a pull request if the ``poetry.lock`` was changed.
* - ``gh-pages.yml``
- Workflow call
- Builds the documentation and deploys it to GitHub Pages.
Expand Down Expand Up @@ -99,6 +102,17 @@ Workflows
CI Actions
----------

Dependency Update
^^^^^^^^^^^^^^^^^

The ``dependency-update.yml`` workflow is used to resolve vulnerabilities by updating our project dependencies.

It can be triggered manually and is also scheduled to run weekly.

The workflow first audits dependencies for known vulnerabilities. If vulnerabilities
are detected, it updates the dependencies using Poetry. When the ``poetry.lock`` is changed,
then it creates a pull request with the update.

.. _ci_yml:

Pull Request
Copy link
Copy Markdown
Collaborator

@ArBridgeman ArBridgeman Apr 15, 2026

Choose a reason for hiding this comment

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

You should also add a changelog entry for this in doc/changes/unreleased.md.
You can check out doc/changes/changes_6.3.0.md for inspiration.

For adding it to the changelog:

  1. Add an entry under the section Features. We tend to keep these to a short sentence description that follows the format:
  • #<GitHub issue number>: <Past tense verb> <clause describing the changes>
  1. Depending on the change, we add a longer description under Summary. In this case, I think it's worthwhile to add a few sentences and link to the documentation . This is because the CI will change for other projects due to this.

Expand Down
100 changes: 100 additions & 0 deletions exasol/toolbox/templates/github/workflows/dependency-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Dependency Update

on:
schedule:
# Every Monday at 03:00 UTC
- cron: "0 3 * * 1"
workflow_dispatch:
Comment thread
ArBridgeman marked this conversation as resolved.

jobs:
dependency-update:
name: Dependency Update
runs-on: "(( os_version ))"
permissions:
contents: write
pull-requests: write

steps:
- name: Check out Repository
id: check-out-repository
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Fail if not running on the default branch
id: check-branch
if: github.ref != format('refs/heads/{0}', github.event.repository.default_branch)
uses: actions/github-script@v8
with:
script: |
core.setFailed('Not running on the default branch. github.ref is ${{ github.ref }}')

- name: Set up Python & Poetry Environment
id: set-up-python-and-poetry-environment
uses: exasol/python-toolbox/.github/actions/python-environment@v6
with:
python-version: "(( minimum_python_version ))"
poetry-version: "(( dependency_manager_version ))"

- name: Audit Dependencies
id: audit-dependencies
run: |
poetry run -- nox -s dependency:audit | tee vulnerabilities.json
LENGTH=$(jq 'length' vulnerabilities.json)
echo "count=$LENGTH" >> "$GITHUB_OUTPUT"

- name: Update Dependencies
id: update-dependencies
if: steps.audit-dependencies.outputs.count > 0
run: poetry update

- name: Check for poetry.lock Changes
id: check-for-poetry-lock-changes
if: steps.audit-dependencies.outputs.count > 0
run: |
if git diff --quiet -- poetry.lock; then
echo "changed=false" >> "$GITHUB_OUTPUT"
else
echo "changed=true" >> "$GITHUB_OUTPUT"
fi

- name: Configure git
id: configure-git
if: steps.check-for-poetry-lock-changes.outputs.changed == 'true'
run: |
git config --global user.email "opensource@exasol.com"
git config --global user.name "Automatic Dependency Updater"

- name: Create branch
id: create-branch
if: steps.check-for-poetry-lock-changes.outputs.changed == 'true'
run: |
branch_name="dependency-update/$(date "+%Y%m%d%H%M%S")"
echo "Creating branch $branch_name"
git switch -C "$branch_name"

- name: Commit changes & push
id: publish-branch
if: steps.check-for-poetry-lock-changes.outputs.changed == 'true'
run: |
branch_name=$(git rev-parse --abbrev-ref HEAD)
git add poetry.lock
git commit --message "Update poetry.lock"
git push --set-upstream origin "$branch_name"

- name: Create pull request
id: create-pr
if: steps.check-for-poetry-lock-changes.outputs.changed == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
BASE_BRANCH=$(gh repo view --json defaultBranchRef -q .defaultBranchRef.name)

gh pr create \
Comment thread
Rimsha2535 marked this conversation as resolved.
--base "$BASE_BRANCH" \
--title "Update dependencies to fix vulnerabilities ($(date '+%Y-%m-%d'))" \
--body "Automated dependency update for \`poetry.lock\`.

This PR was created by the dependency update workflow after running:
- \`poetry run -- nox -s dependency:audit\`
- \`poetry update\`"
2 changes: 1 addition & 1 deletion test/integration/project-template/nox_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ def test_install_github_workflows(self, poetry_path, run_command):
assert output.returncode == 0

file_list = run_command(["ls", ".github/workflows"]).stdout.splitlines()
assert len(file_list) == 13
assert len(file_list) == 14
Comment thread
Rimsha2535 marked this conversation as resolved.
32 changes: 25 additions & 7 deletions test/integration/tools/workflow_integration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def test_with_default(cli_runner):
"check-release-tag\n"
"checks\n"
"ci\n"
"dependency-update\n"
"gh-pages\n"
"matrix-all\n"
"matrix-exasol\n"
Expand All @@ -33,13 +34,28 @@ def test_with_columns(cli_runner):
result = cli_runner.invoke(CLI, ["list", "--columns"])

assert result.exit_code == 0
assert result.output == (
"build-and-publish cd check-release-tag checks ci "
"gh-pages\n"
"matrix-all matrix-exasol matrix-python merge-gate pr-merge "
"report \n"
"slow-checks \n"
)

expected_substrings = """
build-and-publish
cd
check-release-tag
checks
ci
dependency-update
gh-pages
matrix-all
matrix-exasol
matrix-python
merge-gate
pr-merge
report
slow-checks
"""

actual = set(result.output.split())
expected = set(expected_substrings.split())

assert expected.issubset(actual)


def test_show_workflow(cli_runner):
Expand All @@ -57,6 +73,7 @@ def test_show_workflow(cli_runner):
"check-release-tag",
"checks",
"ci",
"dependency-update",
"gh-pages",
"matrix-all",
"matrix-exasol",
Expand Down Expand Up @@ -92,6 +109,7 @@ def test_all_workflows(cli_runner, tmp_path):
"check-release-tag.yml",
"checks.yml",
"ci.yml",
"dependency-update.yml",
"gh-pages.yml",
"matrix-all.yml",
"matrix-exasol.yml",
Expand Down
2 changes: 1 addition & 1 deletion test/unit/nox/_workflow_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class TestGenerateWorkflow:
@staticmethod
@pytest.mark.parametrize(
"nox_session_runner_posargs, expected_count",
[(ALL, 13), *[(key, 1) for key in WORKFLOW_TEMPLATE_OPTIONS.keys()]],
[(ALL, 14), *[(key, 1) for key in WORKFLOW_TEMPLATE_OPTIONS.keys()]],
indirect=["nox_session_runner_posargs"],
)
def test_works_as_expected(
Expand Down
3 changes: 3 additions & 0 deletions test/unit/tool_template_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def test_retrieve_workflow_templates():
"check-release-tag": "check-release-tag.yml",
"checks": "checks.yml",
"ci": "ci.yml",
"dependency-update": "dependency-update.yml",
"gh-pages": "gh-pages.yml",
"matrix-all": "matrix-all.yml",
"matrix-exasol": "matrix-exasol.yml",
Expand Down Expand Up @@ -51,6 +52,7 @@ def test_retrieve_issue_templates():
"check-release-tag": "check-release-tag.yml",
"checks": "checks.yml",
"ci": "ci.yml",
"dependency-update": "dependency-update.yml",
"gh-pages": "gh-pages.yml",
"matrix-all": "matrix-all.yml",
"matrix-exasol": "matrix-exasol.yml",
Expand Down Expand Up @@ -106,6 +108,7 @@ def test_retrieve_templates(subpackage, expected):
"check-release-tag.yml",
"checks.yml",
"ci.yml",
"dependency-update.yml",
"gh-pages.yml",
"matrix-all.yml",
"matrix-exasol.yml",
Expand Down
1 change: 1 addition & 0 deletions test/unit/util/workflows/templates_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def test_get_workflow_templates(project_config):
"check-release-tag",
"checks",
"ci",
"dependency-update",
"gh-pages",
"matrix-all",
"matrix-exasol",
Expand Down