dirsql 0.0.13__tar.gz → 0.0.15__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dirsql-0.0.13 → dirsql-0.0.15}/.claude/CLAUDE.md +8 -5
- dirsql-0.0.15/.github/workflows/minor-release.yml +117 -0
- dirsql-0.0.15/.github/workflows/patch-release.yml +155 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/.github/workflows/publish.yml +16 -6
- {dirsql-0.0.13 → dirsql-0.0.15}/PKG-INFO +1 -1
- {dirsql-0.0.13 → dirsql-0.0.15}/pyproject.toml +3 -1
- dirsql-0.0.15/python/dirsql/__init__.py +6 -0
- dirsql-0.0.15/python/dirsql/_async.py +63 -0
- dirsql-0.0.15/src/lib.rs +645 -0
- dirsql-0.0.15/tests/integration/test_async_dirsql.py +346 -0
- dirsql-0.0.13/.github/workflows/minor-release.yml +0 -48
- dirsql-0.0.13/.github/workflows/patch-release.yml +0 -80
- dirsql-0.0.13/src/lib.rs +0 -291
- {dirsql-0.0.13 → dirsql-0.0.15}/.github/workflows/pr-monitor.yml +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/.github/workflows/python-lint.yml +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/.github/workflows/python-test.yml +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/.github/workflows/rust-test.yml +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/.gitignore +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/.npmignore +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/Cargo.lock +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/Cargo.toml +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/LICENSE +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/SUMMARY.md +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/src/db.rs +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/src/differ.rs +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/src/matcher.rs +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/src/scanner.rs +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/src/watcher.rs +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/tests/__init__.py +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/tests/conftest.py +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/tests/integration/__init__.py +0 -0
- {dirsql-0.0.13 → dirsql-0.0.15}/tests/integration/test_dirsql.py +0 -0
|
@@ -10,6 +10,7 @@ Write scratch/temporary files to `/tmp` instead of asking permission. Use unique
|
|
|
10
10
|
- **NEVER commit directly to main** - always create a PR
|
|
11
11
|
- One PR per bead. Beads should be concise and small -- as small as possible while still being useful
|
|
12
12
|
- Use `bd` (Beads) for task tracking: `bd list`, `bd show <id>`, `bd ready`
|
|
13
|
+
- **Bead first**: When starting new work, the first step is always to create a bead (`bd create`). No implementation work begins without a bead.
|
|
13
14
|
|
|
14
15
|
### Git Worktrees
|
|
15
16
|
|
|
@@ -47,14 +48,15 @@ git worktree remove .worktrees/my-feature
|
|
|
47
48
|
### Subagent Workflow
|
|
48
49
|
|
|
49
50
|
New work on beads should be done via subagents in isolated worktrees. Each subagent:
|
|
50
|
-
1.
|
|
51
|
-
2.
|
|
52
|
-
3.
|
|
53
|
-
4.
|
|
51
|
+
1. Claims the bead (`bd update <id> --claim`) before starting any work
|
|
52
|
+
2. Creates a worktree and branch for its bead
|
|
53
|
+
3. Does the implementation work (red/green TDD)
|
|
54
|
+
4. Pushes the branch and opens a PR
|
|
55
|
+
5. Monitors the PR and proactively resolves:
|
|
54
56
|
- CI failures
|
|
55
57
|
- GPG signing complaints
|
|
56
58
|
- Merge conflicts
|
|
57
|
-
|
|
59
|
+
6. Continues monitoring until the PR is in a mergeable state
|
|
58
60
|
|
|
59
61
|
### Orchestrator Responsibilities
|
|
60
62
|
|
|
@@ -64,6 +66,7 @@ The orchestrator (main Claude session) must proactively:
|
|
|
64
66
|
3. **Handle post-merge cleanup** as soon as a PR merges (pull main, remove worktree, delete branch, close bead).
|
|
65
67
|
4. **Keep the user informed** of PR status without being asked.
|
|
66
68
|
5. **Use foreground monitoring** when waiting on CI and there's no other work to do. Background monitoring causes the conversation to go silent -- use it only when there's genuinely parallel work to perform.
|
|
69
|
+
6. **Scripts to `/tmp`**: For polling/monitoring scripts (watching CI, waiting for merges), write the script to `/tmp` then run it via `bash /tmp/script.sh`. Do not use inline bash loops in tool calls.
|
|
67
70
|
|
|
68
71
|
### Post-Merge Cleanup
|
|
69
72
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
name: Minor Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: write
|
|
8
|
+
id-token: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
detect-changes:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
outputs:
|
|
14
|
+
rust_changed: ${{ steps.changes.outputs.rust_changed }}
|
|
15
|
+
python_changed: ${{ steps.changes.outputs.python_changed }}
|
|
16
|
+
js_changed: ${{ steps.changes.outputs.js_changed }}
|
|
17
|
+
docs_changed: ${{ steps.changes.outputs.docs_changed }}
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v6
|
|
20
|
+
with:
|
|
21
|
+
fetch-depth: 0
|
|
22
|
+
|
|
23
|
+
- name: Detect changed file types since last tag
|
|
24
|
+
id: changes
|
|
25
|
+
run: |
|
|
26
|
+
latest_tag=$(git tag --sort=-v:refname | grep -E '^v[0-9]' | head -n1)
|
|
27
|
+
|
|
28
|
+
if [ -z "$latest_tag" ]; then
|
|
29
|
+
# No tags yet -- treat everything as changed
|
|
30
|
+
echo "rust_changed=true" >> "$GITHUB_OUTPUT"
|
|
31
|
+
echo "python_changed=true" >> "$GITHUB_OUTPUT"
|
|
32
|
+
echo "js_changed=true" >> "$GITHUB_OUTPUT"
|
|
33
|
+
echo "docs_changed=true" >> "$GITHUB_OUTPUT"
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
changed_files=$(git diff --name-only "${latest_tag}..HEAD")
|
|
38
|
+
|
|
39
|
+
if [ -z "$changed_files" ]; then
|
|
40
|
+
echo "rust_changed=false" >> "$GITHUB_OUTPUT"
|
|
41
|
+
echo "python_changed=false" >> "$GITHUB_OUTPUT"
|
|
42
|
+
echo "js_changed=false" >> "$GITHUB_OUTPUT"
|
|
43
|
+
echo "docs_changed=false" >> "$GITHUB_OUTPUT"
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
rust_changed=false
|
|
48
|
+
python_changed=false
|
|
49
|
+
js_changed=false
|
|
50
|
+
docs_changed=false
|
|
51
|
+
|
|
52
|
+
while IFS= read -r file; do
|
|
53
|
+
case "$file" in
|
|
54
|
+
*.rs|Cargo.toml|Cargo.lock) rust_changed=true ;;
|
|
55
|
+
*.py|pyproject.toml|uv.lock) python_changed=true ;;
|
|
56
|
+
package.json|*.ts|*.js) js_changed=true ;;
|
|
57
|
+
README.md|*.md) docs_changed=true ;;
|
|
58
|
+
esac
|
|
59
|
+
done <<< "$changed_files"
|
|
60
|
+
|
|
61
|
+
echo "rust_changed=$rust_changed" >> "$GITHUB_OUTPUT"
|
|
62
|
+
echo "python_changed=$python_changed" >> "$GITHUB_OUTPUT"
|
|
63
|
+
echo "js_changed=$js_changed" >> "$GITHUB_OUTPUT"
|
|
64
|
+
echo "docs_changed=$docs_changed" >> "$GITHUB_OUTPUT"
|
|
65
|
+
|
|
66
|
+
echo "Rust: $rust_changed, Python: $python_changed, JS: $js_changed, Docs: $docs_changed"
|
|
67
|
+
|
|
68
|
+
release:
|
|
69
|
+
needs: detect-changes
|
|
70
|
+
uses: ./.github/workflows/publish.yml
|
|
71
|
+
with:
|
|
72
|
+
bump_type: minor
|
|
73
|
+
publish_pypi: ${{ needs.detect-changes.outputs.rust_changed == 'true' || needs.detect-changes.outputs.python_changed == 'true' || needs.detect-changes.outputs.docs_changed == 'true' }}
|
|
74
|
+
publish_crates: ${{ needs.detect-changes.outputs.rust_changed == 'true' || needs.detect-changes.outputs.docs_changed == 'true' }}
|
|
75
|
+
secrets: inherit
|
|
76
|
+
permissions:
|
|
77
|
+
contents: write
|
|
78
|
+
id-token: write
|
|
79
|
+
|
|
80
|
+
# npm publish inlined here because npm OIDC doesn't support reusable workflows
|
|
81
|
+
publish-npm:
|
|
82
|
+
needs: [detect-changes, release]
|
|
83
|
+
if: >-
|
|
84
|
+
always() &&
|
|
85
|
+
needs.release.outputs.tag_created == 'true' &&
|
|
86
|
+
(needs.detect-changes.outputs.rust_changed == 'true' ||
|
|
87
|
+
needs.detect-changes.outputs.js_changed == 'true' ||
|
|
88
|
+
needs.detect-changes.outputs.docs_changed == 'true')
|
|
89
|
+
runs-on: ubuntu-latest
|
|
90
|
+
permissions:
|
|
91
|
+
contents: read
|
|
92
|
+
id-token: write
|
|
93
|
+
steps:
|
|
94
|
+
- uses: actions/checkout@v6
|
|
95
|
+
|
|
96
|
+
- name: Check if package.json exists
|
|
97
|
+
id: check_npm
|
|
98
|
+
run: |
|
|
99
|
+
if [ -f package.json ]; then
|
|
100
|
+
echo "exists=true" >> $GITHUB_OUTPUT
|
|
101
|
+
else
|
|
102
|
+
echo "exists=false" >> $GITHUB_OUTPUT
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
- name: Setup Node.js
|
|
106
|
+
if: steps.check_npm.outputs.exists == 'true'
|
|
107
|
+
uses: actions/setup-node@v4
|
|
108
|
+
with:
|
|
109
|
+
node-version: '24.x'
|
|
110
|
+
|
|
111
|
+
- name: Update package.json version
|
|
112
|
+
if: steps.check_npm.outputs.exists == 'true'
|
|
113
|
+
run: npm version "${{ needs.release.outputs.new_version }}" --no-git-tag-version
|
|
114
|
+
|
|
115
|
+
- name: Publish to npm
|
|
116
|
+
if: steps.check_npm.outputs.exists == 'true'
|
|
117
|
+
run: npm publish --provenance --access public
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
name: Patch Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
# Run at 2:00 AM UTC every day
|
|
6
|
+
- cron: '0 2 * * *'
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
check:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
outputs:
|
|
19
|
+
should_release: ${{ steps.decide.outputs.should_release }}
|
|
20
|
+
steps:
|
|
21
|
+
- id: decide
|
|
22
|
+
env:
|
|
23
|
+
EVENT: ${{ github.event_name }}
|
|
24
|
+
STRATEGY: ${{ vars.RELEASE_STRATEGY }}
|
|
25
|
+
COMMIT_MSG: ${{ github.event.head_commit.message }}
|
|
26
|
+
run: |
|
|
27
|
+
if [ "$EVENT" = "workflow_dispatch" ]; then
|
|
28
|
+
echo "should_release=true" >> "$GITHUB_OUTPUT"
|
|
29
|
+
elif [ "$EVENT" = "schedule" ] && [ "$STRATEGY" != "immediate" ]; then
|
|
30
|
+
echo "should_release=true" >> "$GITHUB_OUTPUT"
|
|
31
|
+
elif [ "$EVENT" = "push" ] && [ "$STRATEGY" = "immediate" ]; then
|
|
32
|
+
case "$COMMIT_MSG" in
|
|
33
|
+
*'[no-release]'*) echo "should_release=false" >> "$GITHUB_OUTPUT" ;;
|
|
34
|
+
*) echo "should_release=true" >> "$GITHUB_OUTPUT" ;;
|
|
35
|
+
esac
|
|
36
|
+
else
|
|
37
|
+
echo "should_release=false" >> "$GITHUB_OUTPUT"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
detect-changes:
|
|
41
|
+
needs: check
|
|
42
|
+
if: needs.check.outputs.should_release == 'true'
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
outputs:
|
|
45
|
+
rust_changed: ${{ steps.changes.outputs.rust_changed }}
|
|
46
|
+
python_changed: ${{ steps.changes.outputs.python_changed }}
|
|
47
|
+
js_changed: ${{ steps.changes.outputs.js_changed }}
|
|
48
|
+
docs_changed: ${{ steps.changes.outputs.docs_changed }}
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@v6
|
|
51
|
+
with:
|
|
52
|
+
fetch-depth: 0
|
|
53
|
+
|
|
54
|
+
- name: Detect changed file types since last tag
|
|
55
|
+
id: changes
|
|
56
|
+
run: |
|
|
57
|
+
latest_tag=$(git tag --sort=-v:refname | grep -E '^v[0-9]' | head -n1)
|
|
58
|
+
|
|
59
|
+
if [ -z "$latest_tag" ]; then
|
|
60
|
+
# No tags yet -- treat everything as changed
|
|
61
|
+
echo "rust_changed=true" >> "$GITHUB_OUTPUT"
|
|
62
|
+
echo "python_changed=true" >> "$GITHUB_OUTPUT"
|
|
63
|
+
echo "js_changed=true" >> "$GITHUB_OUTPUT"
|
|
64
|
+
echo "docs_changed=true" >> "$GITHUB_OUTPUT"
|
|
65
|
+
exit 0
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
changed_files=$(git diff --name-only "${latest_tag}..HEAD")
|
|
69
|
+
|
|
70
|
+
if [ -z "$changed_files" ]; then
|
|
71
|
+
echo "rust_changed=false" >> "$GITHUB_OUTPUT"
|
|
72
|
+
echo "python_changed=false" >> "$GITHUB_OUTPUT"
|
|
73
|
+
echo "js_changed=false" >> "$GITHUB_OUTPUT"
|
|
74
|
+
echo "docs_changed=false" >> "$GITHUB_OUTPUT"
|
|
75
|
+
exit 0
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
rust_changed=false
|
|
79
|
+
python_changed=false
|
|
80
|
+
js_changed=false
|
|
81
|
+
docs_changed=false
|
|
82
|
+
|
|
83
|
+
while IFS= read -r file; do
|
|
84
|
+
case "$file" in
|
|
85
|
+
*.rs|Cargo.toml|Cargo.lock) rust_changed=true ;;
|
|
86
|
+
*.py|pyproject.toml|uv.lock) python_changed=true ;;
|
|
87
|
+
package.json|*.ts|*.js) js_changed=true ;;
|
|
88
|
+
README.md|*.md) docs_changed=true ;;
|
|
89
|
+
esac
|
|
90
|
+
done <<< "$changed_files"
|
|
91
|
+
|
|
92
|
+
echo "rust_changed=$rust_changed" >> "$GITHUB_OUTPUT"
|
|
93
|
+
echo "python_changed=$python_changed" >> "$GITHUB_OUTPUT"
|
|
94
|
+
echo "js_changed=$js_changed" >> "$GITHUB_OUTPUT"
|
|
95
|
+
echo "docs_changed=$docs_changed" >> "$GITHUB_OUTPUT"
|
|
96
|
+
|
|
97
|
+
echo "Rust: $rust_changed, Python: $python_changed, JS: $js_changed, Docs: $docs_changed"
|
|
98
|
+
|
|
99
|
+
release:
|
|
100
|
+
needs: [check, detect-changes]
|
|
101
|
+
if: >-
|
|
102
|
+
needs.check.outputs.should_release == 'true' &&
|
|
103
|
+
(needs.detect-changes.outputs.rust_changed == 'true' ||
|
|
104
|
+
needs.detect-changes.outputs.python_changed == 'true' ||
|
|
105
|
+
needs.detect-changes.outputs.docs_changed == 'true')
|
|
106
|
+
uses: ./.github/workflows/publish.yml
|
|
107
|
+
with:
|
|
108
|
+
bump_type: patch
|
|
109
|
+
publish_pypi: ${{ needs.detect-changes.outputs.rust_changed == 'true' || needs.detect-changes.outputs.python_changed == 'true' || needs.detect-changes.outputs.docs_changed == 'true' }}
|
|
110
|
+
publish_crates: ${{ needs.detect-changes.outputs.rust_changed == 'true' || needs.detect-changes.outputs.docs_changed == 'true' }}
|
|
111
|
+
secrets: inherit
|
|
112
|
+
permissions:
|
|
113
|
+
contents: write
|
|
114
|
+
id-token: write
|
|
115
|
+
|
|
116
|
+
# npm publish inlined here because npm OIDC doesn't support reusable workflows
|
|
117
|
+
# (it validates the caller workflow name, not the called workflow)
|
|
118
|
+
publish-npm:
|
|
119
|
+
needs: [check, detect-changes, release]
|
|
120
|
+
if: >-
|
|
121
|
+
always() &&
|
|
122
|
+
needs.check.outputs.should_release == 'true' &&
|
|
123
|
+
needs.release.outputs.tag_created == 'true' &&
|
|
124
|
+
(needs.detect-changes.outputs.rust_changed == 'true' ||
|
|
125
|
+
needs.detect-changes.outputs.js_changed == 'true' ||
|
|
126
|
+
needs.detect-changes.outputs.docs_changed == 'true')
|
|
127
|
+
runs-on: ubuntu-latest
|
|
128
|
+
permissions:
|
|
129
|
+
contents: read
|
|
130
|
+
id-token: write
|
|
131
|
+
steps:
|
|
132
|
+
- uses: actions/checkout@v6
|
|
133
|
+
|
|
134
|
+
- name: Check if package.json exists
|
|
135
|
+
id: check_npm
|
|
136
|
+
run: |
|
|
137
|
+
if [ -f package.json ]; then
|
|
138
|
+
echo "exists=true" >> $GITHUB_OUTPUT
|
|
139
|
+
else
|
|
140
|
+
echo "exists=false" >> $GITHUB_OUTPUT
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
- name: Setup Node.js
|
|
144
|
+
if: steps.check_npm.outputs.exists == 'true'
|
|
145
|
+
uses: actions/setup-node@v4
|
|
146
|
+
with:
|
|
147
|
+
node-version: '24.x'
|
|
148
|
+
|
|
149
|
+
- name: Update package.json version
|
|
150
|
+
if: steps.check_npm.outputs.exists == 'true'
|
|
151
|
+
run: npm version "${{ needs.release.outputs.new_version }}" --no-git-tag-version
|
|
152
|
+
|
|
153
|
+
- name: Publish to npm
|
|
154
|
+
if: steps.check_npm.outputs.exists == 'true'
|
|
155
|
+
run: npm publish --provenance --access public
|
|
@@ -7,6 +7,16 @@ on:
|
|
|
7
7
|
description: 'Version bump type: patch or minor'
|
|
8
8
|
required: true
|
|
9
9
|
type: string
|
|
10
|
+
publish_pypi:
|
|
11
|
+
description: 'Whether to publish to PyPI'
|
|
12
|
+
required: false
|
|
13
|
+
type: boolean
|
|
14
|
+
default: true
|
|
15
|
+
publish_crates:
|
|
16
|
+
description: 'Whether to publish to crates.io'
|
|
17
|
+
required: false
|
|
18
|
+
type: boolean
|
|
19
|
+
default: true
|
|
10
20
|
outputs:
|
|
11
21
|
tag_created:
|
|
12
22
|
description: 'Whether a tag was created'
|
|
@@ -127,7 +137,7 @@ jobs:
|
|
|
127
137
|
|
|
128
138
|
build:
|
|
129
139
|
needs: [check-python, tag]
|
|
130
|
-
if: needs.check-python.outputs.has_python == 'true' && needs.tag.outputs.created == 'true'
|
|
140
|
+
if: needs.check-python.outputs.has_python == 'true' && needs.tag.outputs.created == 'true' && inputs.publish_pypi
|
|
131
141
|
runs-on: ${{ matrix.os }}
|
|
132
142
|
strategy:
|
|
133
143
|
fail-fast: false
|
|
@@ -164,7 +174,7 @@ jobs:
|
|
|
164
174
|
|
|
165
175
|
sdist:
|
|
166
176
|
needs: [check-python, tag]
|
|
167
|
-
if: needs.check-python.outputs.has_python == 'true' && needs.tag.outputs.created == 'true'
|
|
177
|
+
if: needs.check-python.outputs.has_python == 'true' && needs.tag.outputs.created == 'true' && inputs.publish_pypi
|
|
168
178
|
runs-on: ubuntu-latest
|
|
169
179
|
steps:
|
|
170
180
|
- uses: actions/checkout@v6
|
|
@@ -187,7 +197,7 @@ jobs:
|
|
|
187
197
|
|
|
188
198
|
publish-pypi:
|
|
189
199
|
needs: [tag, build, sdist]
|
|
190
|
-
if: always() && needs.tag.outputs.created == 'true' && needs.sdist.result == 'success'
|
|
200
|
+
if: always() && needs.tag.outputs.created == 'true' && needs.sdist.result == 'success' && inputs.publish_pypi
|
|
191
201
|
runs-on: ubuntu-latest
|
|
192
202
|
permissions:
|
|
193
203
|
id-token: write
|
|
@@ -211,7 +221,7 @@ jobs:
|
|
|
211
221
|
|
|
212
222
|
publish-crates:
|
|
213
223
|
needs: tag
|
|
214
|
-
if: needs.tag.outputs.created == 'true'
|
|
224
|
+
if: needs.tag.outputs.created == 'true' && inputs.publish_crates
|
|
215
225
|
runs-on: ubuntu-latest
|
|
216
226
|
steps:
|
|
217
227
|
- uses: actions/checkout@v6
|
|
@@ -269,8 +279,8 @@ jobs:
|
|
|
269
279
|
if: |
|
|
270
280
|
always() &&
|
|
271
281
|
needs.tag.outputs.created == 'true' &&
|
|
272
|
-
needs.publish-pypi.result
|
|
273
|
-
|
|
282
|
+
(needs.publish-pypi.result == 'failure' ||
|
|
283
|
+
needs.publish-crates.result == 'failure')
|
|
274
284
|
runs-on: ubuntu-latest
|
|
275
285
|
permissions:
|
|
276
286
|
contents: write
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dirsql"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.15"
|
|
8
8
|
description = "Ephemeral SQL index over a local directory"
|
|
9
9
|
license = "MIT"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -36,3 +36,5 @@ exclude = [
|
|
|
36
36
|
"AGENTS.md",
|
|
37
37
|
"justfile",
|
|
38
38
|
]
|
|
39
|
+
python-source = "python"
|
|
40
|
+
module-name = "dirsql._dirsql"
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Async wrapper for DirSQL."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from dirsql._dirsql import DirSQL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class _WatchStream:
|
|
9
|
+
"""Async iterator that polls for file events."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, db):
|
|
12
|
+
self._db = db
|
|
13
|
+
self._started = False
|
|
14
|
+
self._buffer = []
|
|
15
|
+
|
|
16
|
+
def __aiter__(self):
|
|
17
|
+
return self
|
|
18
|
+
|
|
19
|
+
async def __anext__(self):
|
|
20
|
+
if not self._started:
|
|
21
|
+
await asyncio.to_thread(self._db._start_watcher)
|
|
22
|
+
self._started = True
|
|
23
|
+
|
|
24
|
+
while True:
|
|
25
|
+
if self._buffer:
|
|
26
|
+
return self._buffer.pop(0)
|
|
27
|
+
events = await asyncio.to_thread(self._db._poll_events, 200)
|
|
28
|
+
if events:
|
|
29
|
+
self._buffer.extend(events)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AsyncDirSQL:
|
|
33
|
+
"""Async wrapper around DirSQL.
|
|
34
|
+
|
|
35
|
+
Usage:
|
|
36
|
+
db = await AsyncDirSQL(root, tables=[...])
|
|
37
|
+
results = await db.query("SELECT ...")
|
|
38
|
+
async for event in db.watch():
|
|
39
|
+
...
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, root, *, tables, ignore=None):
|
|
43
|
+
self._root = root
|
|
44
|
+
self._tables = tables
|
|
45
|
+
self._ignore = ignore
|
|
46
|
+
self._db = None
|
|
47
|
+
|
|
48
|
+
def __await__(self):
|
|
49
|
+
return self._init().__await__()
|
|
50
|
+
|
|
51
|
+
async def _init(self):
|
|
52
|
+
self._db = await asyncio.to_thread(
|
|
53
|
+
DirSQL, self._root, tables=self._tables, ignore=self._ignore
|
|
54
|
+
)
|
|
55
|
+
return self
|
|
56
|
+
|
|
57
|
+
async def query(self, sql):
|
|
58
|
+
"""Execute a SQL query asynchronously."""
|
|
59
|
+
return await asyncio.to_thread(self._db.query, sql)
|
|
60
|
+
|
|
61
|
+
def watch(self):
|
|
62
|
+
"""Start watching for file changes. Returns an async iterable of RowEvent."""
|
|
63
|
+
return _WatchStream(self._db)
|