Compare commits

..
2 Commits
3 changed files with 108 additions and 1 deletions
+3 -1
View File
@@ -10,7 +10,7 @@ working across different projects via [VisualMode](https://www.visualmode.dev/).
For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter).
_1844 TILs and counting..._
_1846 TILs and counting..._
See some of the other learning resources I work on:
@@ -486,6 +486,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Capture An Output Value For Use In A Later Step](github-actions/capture-an-output-value-for-use-in-a-later-step.md)
- [Disable A Workflow With The gh CLI](github-actions/disable-a-workflow-with-the-gh-cli.md)
- [Reference An Encrypted Secret In An Action](github-actions/reference-an-encrypted-secret-in-an-action.md)
- [Run Schedule Action To Commit Regular Updates](github-actions/run-scheduled-action-to-commit-regular-updates.md)
- [Trigger A Workflow Via An API Call](github-actions/trigger-a-workflow-via-an-api-call.md)
- [Use Labels To Block PR Merge](github-actions/use-labels-to-block-pr-merge.md)
@@ -1072,6 +1073,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Access Most Recent Return Value In REPL](python/access-most-recent-return-value-in-repl.md)
- [Access SQLite Result Values By Name With Row Factory](python/access-sqlite-result-values-by-name-with-row-factory.md)
- [Access Variables Outside Loop Scope](python/access-variables-outside-loop-scope.md)
- [Another Way To Mark Keyword-Only Dataclass Fields](python/another-way-to-mark-keyword-only-dataclass-fields.md)
- [Argument Defaults Are Evaluated When Function Is Defined](python/argument-defaults-are-evaluated-when-function-is-defined.md)
- [Assert Is Only A Development Check](python/assert-is-only-a-development-check.md)
- [Avoid Modification With Frozen Dataclass](python/avoid-modification-with-frozen-dataclass.md)
@@ -0,0 +1,58 @@
# Run Scheduled Action To Commit Regular Updates
The quintessential example of what GitHub Actions are used for is running CI
tasks like the test suite, type checker, linter, etc. Each CI step runs to
completion and either passes or fails and you see the results in the GitHub PR
interface. That's a great use case, but far from the only one. GitHub Actions
are a much more general-purpose execution environment that can be used for much
more.
Here is a recent example that expanded my mind a bit on what is possible with
GitHub actions. Imagine a GitHub Action that is scheduled to run once a day, it
executes a script that might make changes to the repo itself (e.g. the
`README.md`), and then commits those changes (self-updating the repo).
Here is a minimal version of a workflow that does that:
```yaml
on:
schedule:
- cron: '17 11 * * *' # daily at 11:17 UTC (06:17 CT)
workflow_dispatch:
permissions:
contents: write
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # get the repo + push credentials
- run: ./bin/regenerate_readme.sh # apply updates to README
- run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add -A README.md
git diff --staged --quiet || {
git commit -m "chore: regenerate README with latest updates"
git push
} # commit updates to README if there are any, otherwise no-op
```
Once a day at 11:17 UTC, GitHub Actions will run this workflow which is made up
of a single job with permissions to write to the repo.
- First, it checks out the repo in the job container which includes push
credentials.
- Then it runs a script that might update the README (e.g. maybe based on other
things that have since been committed to the repo).
- Last, it attempts to stage any changes the script made to the README. If there
are any, then it will commit them with that generic commit message. Finally it
will push that commit to the main branch.
If this all sounds a bit untethered from a real-world example, then take a look
at how I use this exact pattern to [apply daily updates to my GitHub Profile
README](https://github.com/jbranchaud/jbranchaud/blob/main/.github/workflows/update-tils.yml)
based on the latest [TILs](https://github.com/jbranchaud/til) I have written.
@@ -0,0 +1,47 @@
# Another Way To Mark Keyword-Only Dataclass Fields
In [Configure Other Attributes Of Dataclass
Field](configure-other-attributes-of-dataclass-field), I showed how the
[`dataclasses.field`](https://docs.python.org/3/library/dataclasses.html#dataclasses.field)
constructor function can be used. One of the parameters I demonstrated was
`kw_only`. Each field constructed with `kw_only=True` will be required to be
passed as a keyword-only parameter when constructing an instance of that
`dataclass`.
Another way to specify keyword-only parameters with `dataclass` fields is to
segment them with `KW_ONLY`. This sentinel value can be included as a
pseudo-field where all fields that come after it are treated as keyword-only.
Translating the example from that other post would look like this:
```python
from dataclasses import dataclass, field, KW_ONLY
from datetime import datetime
@dataclass
class Session:
start_time: datetime
project_name: str
_: KW_ONLY
tags: list[str] = field(default_factory=list, kw_only=True)
end_time: datetime | None = None
# ...
sesh1 = Session(start1, "my-project", tags=["pytorch", "numpy"])
sesh2 = Session(start2, "other-project", end_time=datetime.now())
```
The field whose value is `KW_ONLY` is only used to signal that keyword-only
boundary. It does not itself become a field of the dataclass.
On the one hand I like this approach because it feels closer to [the way this is
signaled in standard function definition
syntax](force-remaining-arguments-to-be-named.md).
```python
def build_session(start_time, project_name, *, tags, end_time=None)
```
On the other hand, it feels like magic `dataclass` syntax whereas the
`kw_only=True` parameter is more explicit.