codestrain 0.1.2__tar.gz → 0.1.4__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.
- {codestrain-0.1.2 → codestrain-0.1.4}/.github/workflows/ci.yml +4 -0
- codestrain-0.1.4/.github/workflows/release.yml +125 -0
- {codestrain-0.1.2 → codestrain-0.1.4}/PKG-INFO +1 -1
- {codestrain-0.1.2 → codestrain-0.1.4}/codestrain_cli.py +91 -16
- {codestrain-0.1.2 → codestrain-0.1.4}/pyproject.toml +1 -1
- {codestrain-0.1.2 → codestrain-0.1.4}/tests/smoke.sh +7 -0
- {codestrain-0.1.2 → codestrain-0.1.4}/tests/test_unit.py +51 -0
- codestrain-0.1.2/.github/workflows/release.yml +0 -80
- {codestrain-0.1.2 → codestrain-0.1.4}/.assets/logo.png +0 -0
- {codestrain-0.1.2 → codestrain-0.1.4}/.gitignore +0 -0
- {codestrain-0.1.2 → codestrain-0.1.4}/CONTRIBUTING.md +0 -0
- {codestrain-0.1.2 → codestrain-0.1.4}/LICENSE +0 -0
- {codestrain-0.1.2 → codestrain-0.1.4}/README.md +0 -0
- {codestrain-0.1.2 → codestrain-0.1.4}/TESTING.md +0 -0
- {codestrain-0.1.2 → codestrain-0.1.4}/tests/__init__.py +0 -0
- {codestrain-0.1.2 → codestrain-0.1.4}/tests/conftest.py +0 -0
- {codestrain-0.1.2 → codestrain-0.1.4}/tests/fixtures/projects/-Users-test-projectA/session-001.jsonl +0 -0
- {codestrain-0.1.2 → codestrain-0.1.4}/tests/fixtures/projects/-Users-test-projectA/session-002.jsonl +0 -0
- {codestrain-0.1.2 → codestrain-0.1.4}/tests/fixtures/projects/-Users-test-projectB/session-003.jsonl +0 -0
- {codestrain-0.1.2 → codestrain-0.1.4}/tests/fixtures/projects/empty-project/empty.jsonl +0 -0
- {codestrain-0.1.2 → codestrain-0.1.4}/tests/test_jsonl.py +0 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
# Opt into Node.js 24 for JS-based actions; default will flip on 2026-06-02.
|
|
9
|
+
# See https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
|
|
10
|
+
env:
|
|
11
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build:
|
|
15
|
+
name: Build distributions
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.12"
|
|
25
|
+
|
|
26
|
+
- name: Install build tooling
|
|
27
|
+
run: python -m pip install --upgrade build
|
|
28
|
+
|
|
29
|
+
- name: Build sdist and wheel
|
|
30
|
+
working-directory: .
|
|
31
|
+
run: python -m build
|
|
32
|
+
|
|
33
|
+
- name: Upload distributions
|
|
34
|
+
uses: actions/upload-artifact@v4
|
|
35
|
+
with:
|
|
36
|
+
name: dist
|
|
37
|
+
path: dist/
|
|
38
|
+
|
|
39
|
+
publish:
|
|
40
|
+
name: Publish to PyPI
|
|
41
|
+
needs: build
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
environment: pypi
|
|
44
|
+
permissions:
|
|
45
|
+
id-token: write
|
|
46
|
+
steps:
|
|
47
|
+
- name: Download distributions
|
|
48
|
+
uses: actions/download-artifact@v4
|
|
49
|
+
with:
|
|
50
|
+
name: dist
|
|
51
|
+
path: dist/
|
|
52
|
+
|
|
53
|
+
- name: Publish to PyPI via Trusted Publisher
|
|
54
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
55
|
+
|
|
56
|
+
bump-homebrew:
|
|
57
|
+
name: Bump Homebrew Formula
|
|
58
|
+
needs: publish
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
if: ${{ !contains(github.ref_name, 'rc') && !contains(github.ref_name, 'a') && !contains(github.ref_name, 'b') }}
|
|
61
|
+
steps:
|
|
62
|
+
# mislav/bump-homebrew-formula-action proved flaky against PyPI's
|
|
63
|
+
# /packages/source/ redirect (HTTP 404 on its HEAD probe even after
|
|
64
|
+
# the file is reachable via GET). Rolling our own bump is more
|
|
65
|
+
# reliable and easier to debug: query PyPI's JSON API for the
|
|
66
|
+
# canonical URL + sha256, then sed-update the formula and push.
|
|
67
|
+
|
|
68
|
+
- name: Wait for PyPI propagation
|
|
69
|
+
run: sleep 60
|
|
70
|
+
|
|
71
|
+
- name: Fetch PyPI sdist metadata
|
|
72
|
+
id: pypi
|
|
73
|
+
run: |
|
|
74
|
+
set -eu
|
|
75
|
+
VERSION="${GITHUB_REF_NAME#v}"
|
|
76
|
+
echo "Looking up codestrain==$VERSION on PyPI..."
|
|
77
|
+
# Retry up to 6 times (1 min total) in case the metadata
|
|
78
|
+
# endpoint lags behind the file CDN.
|
|
79
|
+
for i in 1 2 3 4 5 6; do
|
|
80
|
+
BODY=$(curl -fsS "https://pypi.org/pypi/codestrain/$VERSION/json" 2>/dev/null) && break
|
|
81
|
+
echo " attempt $i failed; sleeping 10s"
|
|
82
|
+
sleep 10
|
|
83
|
+
done
|
|
84
|
+
if [ -z "${BODY:-}" ]; then
|
|
85
|
+
echo "::error::PyPI metadata for codestrain==$VERSION never appeared"
|
|
86
|
+
exit 1
|
|
87
|
+
fi
|
|
88
|
+
URL=$(echo "$BODY" | jq -r '.urls[] | select(.packagetype=="sdist") | .url')
|
|
89
|
+
SHA=$(echo "$BODY" | jq -r '.urls[] | select(.packagetype=="sdist") | .digests.sha256')
|
|
90
|
+
echo " url=$URL"
|
|
91
|
+
echo " sha=$SHA"
|
|
92
|
+
{
|
|
93
|
+
echo "version=$VERSION"
|
|
94
|
+
echo "url=$URL"
|
|
95
|
+
echo "sha=$SHA"
|
|
96
|
+
} >> "$GITHUB_OUTPUT"
|
|
97
|
+
|
|
98
|
+
- name: Update Formula in codestrain/homebrew-tap
|
|
99
|
+
env:
|
|
100
|
+
COMMITTER_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
|
|
101
|
+
VERSION: ${{ steps.pypi.outputs.version }}
|
|
102
|
+
URL: ${{ steps.pypi.outputs.url }}
|
|
103
|
+
SHA: ${{ steps.pypi.outputs.sha }}
|
|
104
|
+
run: |
|
|
105
|
+
set -eu
|
|
106
|
+
git clone --depth 1 \
|
|
107
|
+
"https://x-access-token:${COMMITTER_TOKEN}@github.com/codestrain/homebrew-tap.git" tap
|
|
108
|
+
cd tap
|
|
109
|
+
# sed has to use a delimiter that doesn't appear in URLs (|).
|
|
110
|
+
sed -i -E "s|^ url \".*\"| url \"${URL}\"|" Formula/codestrain.rb
|
|
111
|
+
sed -i -E "s|^ sha256 \".*\"| sha256 \"${SHA}\"|" Formula/codestrain.rb
|
|
112
|
+
echo "=== updated formula ==="
|
|
113
|
+
head -12 Formula/codestrain.rb
|
|
114
|
+
git config user.email "actions@github.com"
|
|
115
|
+
git config user.name "github-actions[bot]"
|
|
116
|
+
git add Formula/codestrain.rb
|
|
117
|
+
if git diff --cached --quiet; then
|
|
118
|
+
echo "no formula change — skipping commit"
|
|
119
|
+
exit 0
|
|
120
|
+
fi
|
|
121
|
+
git commit -m "codestrain ${VERSION}
|
|
122
|
+
|
|
123
|
+
Auto-bumped from PyPI sdist for tag ${GITHUB_REF_NAME}.
|
|
124
|
+
Source: https://github.com/codestrain/codestrain-cli/releases/tag/${GITHUB_REF_NAME}"
|
|
125
|
+
git push
|
|
@@ -707,6 +707,49 @@ def print_project_breakdown(project_stats, anonymize=False):
|
|
|
707
707
|
print()
|
|
708
708
|
|
|
709
709
|
|
|
710
|
+
# ── Share encoding ───────────────────────────────────────────────────────────
|
|
711
|
+
#
|
|
712
|
+
# `codestrain --share` produces a single self-contained URL that anyone can
|
|
713
|
+
# open to see the same anonymized report. No server, no upload, no cookies —
|
|
714
|
+
# the whole report is gzip+base64-encoded into the URL query string. Decoded
|
|
715
|
+
# client-side by codestrain.dev/s/index.html.
|
|
716
|
+
|
|
717
|
+
SHARE_BASE_URL = "https://codestrain.dev/s/"
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
def build_share_url(text: str, base: str = SHARE_BASE_URL) -> str:
|
|
721
|
+
"""Encode `text` into a self-contained share URL.
|
|
722
|
+
|
|
723
|
+
Pipeline: utf-8 → gzip (level 9, deterministic) → base64-urlsafe (strip `=`
|
|
724
|
+
padding). The frontend reverses this with DecompressionStream('gzip').
|
|
725
|
+
Average codestrain --all output (~1.5 KB raw) compresses to ~600 B → ~800 B
|
|
726
|
+
base64 → final URL ~ 850 B. Well within all known browser URL limits
|
|
727
|
+
(Chrome / Safari / Firefox / curl all accept up to ~32 KB).
|
|
728
|
+
"""
|
|
729
|
+
import base64
|
|
730
|
+
import gzip
|
|
731
|
+
payload = gzip.compress(text.encode("utf-8"), compresslevel=9, mtime=0)
|
|
732
|
+
encoded = base64.urlsafe_b64encode(payload).decode("ascii").rstrip("=")
|
|
733
|
+
return f"{base.rstrip('/')}/?d={encoded}"
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
def _copy_to_clipboard(text: str) -> bool:
|
|
737
|
+
"""Best-effort clipboard copy. Returns True on success, False if no
|
|
738
|
+
suitable system command is available. Never raises.
|
|
739
|
+
"""
|
|
740
|
+
import shutil
|
|
741
|
+
import subprocess
|
|
742
|
+
for cmd in (["pbcopy"], ["xclip", "-selection", "clipboard"], ["wl-copy"]):
|
|
743
|
+
if shutil.which(cmd[0]):
|
|
744
|
+
try:
|
|
745
|
+
p = subprocess.run(cmd, input=text.encode("utf-8"), timeout=2)
|
|
746
|
+
if p.returncode == 0:
|
|
747
|
+
return True
|
|
748
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
749
|
+
continue
|
|
750
|
+
return False
|
|
751
|
+
|
|
752
|
+
|
|
710
753
|
# ── Main ─────────────────────────────────────────────────────────────────────
|
|
711
754
|
|
|
712
755
|
def main():
|
|
@@ -766,9 +809,21 @@ examples:
|
|
|
766
809
|
help="Logo variant: auto (default, adapts to terminal width), "
|
|
767
810
|
"big (5-line ASCII), small (one-line), or none (skip logo)",
|
|
768
811
|
)
|
|
812
|
+
parser.add_argument(
|
|
813
|
+
"--share",
|
|
814
|
+
action="store_true",
|
|
815
|
+
help="Generate a shareable codestrain.dev URL with anonymized stats "
|
|
816
|
+
"(implies --anonymize --no-color; nothing is uploaded — all data "
|
|
817
|
+
"is encoded in the URL itself)",
|
|
818
|
+
)
|
|
769
819
|
|
|
770
820
|
args = parser.parse_args()
|
|
771
821
|
|
|
822
|
+
# --share is a shortcut that anonymizes, strips color, then prints a URL.
|
|
823
|
+
if args.share:
|
|
824
|
+
args.anonymize = True
|
|
825
|
+
args.no_color = True
|
|
826
|
+
|
|
772
827
|
if args.no_color:
|
|
773
828
|
global _colors_on
|
|
774
829
|
_colors_on = False
|
|
@@ -852,23 +907,43 @@ examples:
|
|
|
852
907
|
project_stats[project_name] = []
|
|
853
908
|
project_stats[project_name].append(stats)
|
|
854
909
|
|
|
855
|
-
# Display results
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
910
|
+
# Display results — capture stdout into a buffer if --share, so we can
|
|
911
|
+
# encode the rendered report into a URL after the run.
|
|
912
|
+
import contextlib
|
|
913
|
+
import io as _io
|
|
914
|
+
|
|
915
|
+
def _render_to(stream):
|
|
916
|
+
with contextlib.redirect_stdout(stream):
|
|
917
|
+
print_header_adaptive(args.logo)
|
|
918
|
+
time_label = "Today" if not args.all else "All Time"
|
|
919
|
+
if args.project and not args.anonymize:
|
|
920
|
+
time_label += f" (project: {args.project})"
|
|
921
|
+
elif args.project and args.anonymize:
|
|
922
|
+
time_label += " (filtered)"
|
|
923
|
+
print_divider(time_label)
|
|
924
|
+
print()
|
|
925
|
+
print_session_summary(all_stats)
|
|
926
|
+
if len(project_stats) > 1 and not args.no_breakdown:
|
|
927
|
+
print_project_breakdown(project_stats, anonymize=args.anonymize)
|
|
928
|
+
print()
|
|
870
929
|
|
|
871
|
-
|
|
930
|
+
if args.share:
|
|
931
|
+
buf = _io.StringIO()
|
|
932
|
+
_render_to(buf)
|
|
933
|
+
report_text = buf.getvalue()
|
|
934
|
+
# Print the report to stdout so the user sees what they're sharing
|
|
935
|
+
sys.stdout.write(report_text)
|
|
936
|
+
share_url = build_share_url(report_text)
|
|
937
|
+
print()
|
|
938
|
+
print(f" Shareable URL ({len(share_url)} chars):")
|
|
939
|
+
print(f" {share_url}")
|
|
940
|
+
print()
|
|
941
|
+
# Best-effort clipboard copy on macOS / Linux
|
|
942
|
+
if _copy_to_clipboard(share_url):
|
|
943
|
+
print(" (copied to clipboard)")
|
|
944
|
+
print()
|
|
945
|
+
else:
|
|
946
|
+
_render_to(sys.stdout)
|
|
872
947
|
|
|
873
948
|
|
|
874
949
|
if __name__ == "__main__":
|
|
@@ -107,6 +107,13 @@ out=$("$PY" "$CLI" --path "$FIXTURES" --all --project projectA --no-color 2>&1)
|
|
|
107
107
|
# format_tokens compacts to "3.8K"-style — assert the K
|
|
108
108
|
contains "$out" "3.8K" "expected ~3800 input tokens for projectA (compact = 3.8K)"
|
|
109
109
|
|
|
110
|
+
# 9. --share emits a codestrain.dev/s/ URL and implies --anonymize.
|
|
111
|
+
out=$("$PY" "$CLI" --path "$FIXTURES" --all --share 2>&1)
|
|
112
|
+
contains "$out" "https://codestrain.dev/s/?d=" "--share prints shareable URL"
|
|
113
|
+
contains "$out" "project-1" "--share implies --anonymize (project-1)"
|
|
114
|
+
not_contains "$out" "projectA" "--share scrubs real project names"
|
|
115
|
+
not_contains "$out" "projectB" "--share scrubs real project names"
|
|
116
|
+
|
|
110
117
|
# ----------------------------------------------------------------------------
|
|
111
118
|
# summary
|
|
112
119
|
# ----------------------------------------------------------------------------
|
|
@@ -145,3 +145,54 @@ def test_format_cost_dollars(cli, cost, expected_prefix):
|
|
|
145
145
|
def test_format_tokens_compact(cli, n, expected_substr):
|
|
146
146
|
out = cli.format_tokens(n)
|
|
147
147
|
assert expected_substr in out
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# ─── Share encoding ─────────────────────────────────────────────────────────
|
|
151
|
+
|
|
152
|
+
def test_share_url_round_trip(cli):
|
|
153
|
+
"""gzip + base64-urlsafe encoder must round-trip a real report string."""
|
|
154
|
+
import base64
|
|
155
|
+
import gzip
|
|
156
|
+
sample = (
|
|
157
|
+
"--- All Time ----\n"
|
|
158
|
+
" Sessions: 1454\n"
|
|
159
|
+
" Duration: 137h 21m\n"
|
|
160
|
+
" Cost: $21948.61\n"
|
|
161
|
+
)
|
|
162
|
+
url = cli.build_share_url(sample)
|
|
163
|
+
assert url.startswith("https://codestrain.dev/s/?d=")
|
|
164
|
+
encoded = url.split("?d=", 1)[1]
|
|
165
|
+
# Restore stripped padding + base64-urlsafe → bytes → gunzip → text.
|
|
166
|
+
pad = "=" * (-len(encoded) % 4)
|
|
167
|
+
raw = base64.urlsafe_b64decode(encoded + pad)
|
|
168
|
+
decoded = gzip.decompress(raw).decode("utf-8")
|
|
169
|
+
assert decoded == sample
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def test_share_url_unicode_safe(cli):
|
|
173
|
+
"""Non-ASCII content (Cyrillic, emoji, currency) must survive the trip."""
|
|
174
|
+
import base64
|
|
175
|
+
import gzip
|
|
176
|
+
sample = "Сессий: 1454 · стоимость €18 950 / $21 948 · 🟢 recovery 82%"
|
|
177
|
+
url = cli.build_share_url(sample)
|
|
178
|
+
encoded = url.split("?d=", 1)[1]
|
|
179
|
+
pad = "=" * (-len(encoded) % 4)
|
|
180
|
+
raw = base64.urlsafe_b64decode(encoded + pad)
|
|
181
|
+
assert gzip.decompress(raw).decode("utf-8") == sample
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def test_share_url_compact_for_typical_report(cli):
|
|
185
|
+
"""A realistic 1.5 KB report should encode to under ~1 KB URL."""
|
|
186
|
+
# 30 lines × 50 chars = 1500 bytes (close to real --all output).
|
|
187
|
+
sample = "\n".join(f" line {i:02d}: project-{i} 12h 34m $123.45" for i in range(30))
|
|
188
|
+
url = cli.build_share_url(sample)
|
|
189
|
+
# Comfortably under 32 KB (the conservative browser URL limit).
|
|
190
|
+
assert len(url) < 2_000
|
|
191
|
+
# Should compress reasonably — the line template is highly repetitive.
|
|
192
|
+
assert len(url) < len(sample)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def test_share_url_deterministic(cli):
|
|
196
|
+
"""Same input → same URL across calls (mtime=0 in gzip header)."""
|
|
197
|
+
text = "hello world\n"
|
|
198
|
+
assert cli.build_share_url(text) == cli.build_share_url(text)
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
name: Release
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags:
|
|
6
|
-
- "v*"
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
build:
|
|
10
|
-
name: Build distributions
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
steps:
|
|
13
|
-
- name: Checkout
|
|
14
|
-
uses: actions/checkout@v4
|
|
15
|
-
|
|
16
|
-
- name: Set up Python
|
|
17
|
-
uses: actions/setup-python@v5
|
|
18
|
-
with:
|
|
19
|
-
python-version: "3.12"
|
|
20
|
-
|
|
21
|
-
- name: Install build tooling
|
|
22
|
-
run: python -m pip install --upgrade build
|
|
23
|
-
|
|
24
|
-
- name: Build sdist and wheel
|
|
25
|
-
working-directory: .
|
|
26
|
-
run: python -m build
|
|
27
|
-
|
|
28
|
-
- name: Upload distributions
|
|
29
|
-
uses: actions/upload-artifact@v4
|
|
30
|
-
with:
|
|
31
|
-
name: dist
|
|
32
|
-
path: dist/
|
|
33
|
-
|
|
34
|
-
publish:
|
|
35
|
-
name: Publish to PyPI
|
|
36
|
-
needs: build
|
|
37
|
-
runs-on: ubuntu-latest
|
|
38
|
-
environment: pypi
|
|
39
|
-
permissions:
|
|
40
|
-
id-token: write
|
|
41
|
-
steps:
|
|
42
|
-
- name: Download distributions
|
|
43
|
-
uses: actions/download-artifact@v4
|
|
44
|
-
with:
|
|
45
|
-
name: dist
|
|
46
|
-
path: dist/
|
|
47
|
-
|
|
48
|
-
- name: Publish to PyPI via Trusted Publisher
|
|
49
|
-
uses: pypa/gh-action-pypi-publish@release/v1
|
|
50
|
-
|
|
51
|
-
bump-homebrew:
|
|
52
|
-
name: Bump Homebrew Formula
|
|
53
|
-
needs: publish
|
|
54
|
-
runs-on: ubuntu-latest
|
|
55
|
-
if: ${{ !contains(github.ref_name, 'rc') && !contains(github.ref_name, 'a') && !contains(github.ref_name, 'b') }}
|
|
56
|
-
steps:
|
|
57
|
-
# Wait briefly so the new sdist is queryable on PyPI's CDN.
|
|
58
|
-
- name: Wait for PyPI propagation
|
|
59
|
-
run: sleep 30
|
|
60
|
-
|
|
61
|
-
- name: Open PR against codestrain/homebrew-tap
|
|
62
|
-
uses: mislav/bump-homebrew-formula-action@v3
|
|
63
|
-
with:
|
|
64
|
-
formula-name: codestrain
|
|
65
|
-
formula-path: Formula/codestrain.rb
|
|
66
|
-
homebrew-tap: codestrain/homebrew-tap
|
|
67
|
-
base-branch: main
|
|
68
|
-
# Compute download URL from the just-published PyPI sdist.
|
|
69
|
-
# mislav/bump-homebrew-formula-action auto-strips `v` prefix from
|
|
70
|
-
# github.ref_name, so v0.1.2 → 0.1.2 in the URL.
|
|
71
|
-
download-url: https://files.pythonhosted.org/packages/source/c/codestrain/codestrain-${{ github.ref_name }}.tar.gz
|
|
72
|
-
commit-message: |
|
|
73
|
-
codestrain {{version}}
|
|
74
|
-
|
|
75
|
-
Auto-bumped by mislav/bump-homebrew-formula-action.
|
|
76
|
-
Source: https://github.com/codestrain/codestrain-cli/releases/tag/${{ github.ref_name }}
|
|
77
|
-
env:
|
|
78
|
-
# Fine-grained PAT with `Contents: write` on codestrain/homebrew-tap.
|
|
79
|
-
# Stored as a repo secret in codestrain/codestrain-cli.
|
|
80
|
-
COMMITTER_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codestrain-0.1.2 → codestrain-0.1.4}/tests/fixtures/projects/-Users-test-projectA/session-001.jsonl
RENAMED
|
File without changes
|
{codestrain-0.1.2 → codestrain-0.1.4}/tests/fixtures/projects/-Users-test-projectA/session-002.jsonl
RENAMED
|
File without changes
|
{codestrain-0.1.2 → codestrain-0.1.4}/tests/fixtures/projects/-Users-test-projectB/session-003.jsonl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|