Skip to content

Commit a5ae243

Browse files
authored
ci: add automation to python repo (#94)
* ci: add automation to python repo * fix: config update due to updated pytest-asyncio
1 parent 84fc11f commit a5ae243

13 files changed

+220
-80
lines changed

.github/CODEOWNERS

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
* @mahboubii @gumuz
1+
* @ferhatelmas @gumuz

.github/workflows/ci.yml

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ concurrency:
1111

1212
jobs:
1313
build:
14+
name: 🧪 Test & lint
1415
runs-on: ubuntu-latest
1516
strategy:
1617
max-parallel: 1
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Create release PR
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: "The new version number with 'v' prefix. Example: v1.40.1"
8+
required: true
9+
10+
jobs:
11+
init_release:
12+
name: 🚀 Create release PR
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v2
16+
with:
17+
fetch-depth: 0 # gives the changelog generator access to all previous commits
18+
19+
- name: Update CHANGELOG.md, __pkg__.py and push release branch
20+
env:
21+
VERSION: ${{ github.event.inputs.version }}
22+
run: |
23+
npx --yes [email protected] --release-as "$VERSION" --skip.tag --skip.commit --tag-prefix=v
24+
git config --global user.name 'github-actions'
25+
git config --global user.email '[email protected]'
26+
git checkout -q -b "release-$VERSION"
27+
git commit -am "chore(release): $VERSION"
28+
git push -q -u origin "release-$VERSION"
29+
30+
- name: Get changelog diff
31+
uses: actions/github-script@v5
32+
with:
33+
script: |
34+
const get_change_log_diff = require('./scripts/get_changelog_diff.js')
35+
core.exportVariable('CHANGELOG', get_change_log_diff())
36+
37+
- name: Open pull request
38+
env:
39+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40+
run: |
41+
gh pr create \
42+
-t "Release ${{ github.event.inputs.version }}" \
43+
-b "# :rocket: ${{ github.event.inputs.version }}
44+
Make sure to use squash & merge when merging!
45+
Once this is merged, another job will kick off automatically and publish the package.
46+
# :memo: Changelog
47+
${{ env.CHANGELOG }}"

.github/workflows/release.yml

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Release
2+
3+
on:
4+
pull_request:
5+
types: [closed]
6+
branches:
7+
- master
8+
9+
jobs:
10+
Release:
11+
name: 🚀 Release
12+
if: github.event.pull_request.merged && startsWith(github.head_ref, 'release-')
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v2
16+
with:
17+
fetch-depth: 0
18+
19+
- uses: actions/github-script@v5
20+
with:
21+
script: |
22+
const get_change_log_diff = require('./scripts/get_changelog_diff.js')
23+
core.exportVariable('CHANGELOG', get_change_log_diff())
24+
25+
// Getting the release version from the PR source branch
26+
// Source branch looks like this: release-1.0.0
27+
const version = context.payload.pull_request.head.ref.split('-')[1]
28+
core.exportVariable('VERSION', version)
29+
30+
- uses: actions/setup-python@v2
31+
with:
32+
python-version: "3.10"
33+
34+
- name: Publish to PyPi
35+
env:
36+
TWINE_USERNAME: "__token__"
37+
TWINE_PASSWORD: "${{ secrets.PYPI_TOKEN }}"
38+
run: |
39+
pip install -q twine==3.7.1
40+
python setup.py sdist bdist_wheel
41+
twine upload --non-interactive dist/*
42+
43+
- name: Create release on GitHub
44+
uses: ncipollo/release-action@v1
45+
with:
46+
body: ${{ env.CHANGELOG }}
47+
tag: ${{ env.VERSION }}
48+
token: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/reviewdog.yml

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ concurrency:
88

99
jobs:
1010
reviewdog:
11+
name: 🐶 Reviewdog
1112
runs-on: ubuntu-latest
1213
steps:
1314
- uses: actions/checkout@v2

.versionrc.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const pkgUpdater = {
2+
VERSION_REGEX: /__version__ = "(.+)"/,
3+
4+
readVersion: function (contents) {
5+
const version = this.VERSION_REGEX.exec(contents)[1];
6+
return version;
7+
},
8+
9+
writeVersion: function (contents, version) {
10+
return contents.replace(this.VERSION_REGEX.exec(contents)[0], `__version__ = "${version}"`);
11+
}
12+
}
13+
14+
module.exports = {
15+
bumpFiles: [{ filename: './stream_chat/__pkg__.py', updater: pkgUpdater }],
16+
}

CHANGELOG.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
## Jan 6, 2022 - 3.17.0
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4+
5+
## [3.17.0](https://github.com/GetStream/stream-chat-python/compare/v3.16.0...v3.17.0) (2022-01-06)
26

37
- Add options support into channel truncate
48
- Add options support into add members

pyproject.toml

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ exclude = '''
2424
)/
2525
'''
2626

27+
[tool.pytest.ini_options]
28+
testpaths = ["stream_chat/tests"]
29+
asyncio_mode = "auto"
30+
2731
[tool.mypy]
2832
disallow_untyped_defs = true
2933
disallow_untyped_calls = true

scripts/get_changelog_diff.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
Here we're trying to parse the latest changes from CHANGELOG.md file.
3+
The changelog looks like this:
4+
5+
## 0.0.3
6+
- Something #3
7+
## 0.0.2
8+
- Something #2
9+
## 0.0.1
10+
- Something #1
11+
12+
In this case we're trying to extract "- Something #3" since that's the latest change.
13+
*/
14+
module.exports = () => {
15+
const fs = require('fs')
16+
17+
changelog = fs.readFileSync('CHANGELOG.md', 'utf8')
18+
releases = changelog.match(/## [?[0-9](.+)/g)
19+
20+
current_release = changelog.indexOf(releases[0])
21+
previous_release = changelog.indexOf(releases[1])
22+
23+
latest_changes = changelog.substr(current_release, previous_release - current_release)
24+
25+
return latest_changes
26+
}

stream_chat/tests/async_chat/test_channel.py

-27
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
@pytest.mark.incremental
1212
class TestChannel(object):
13-
@pytest.mark.asyncio
1413
async def test_ban_user(
1514
self, channel: Channel, random_user: Dict, server_user: Dict
1615
):
@@ -23,7 +22,6 @@ async def test_ban_user(
2322
)
2423
await channel.unban_user(random_user["id"])
2524

26-
@pytest.mark.asyncio
2725
async def test_create_without_id(
2826
self, client: StreamChatAsync, random_users: List[Dict]
2927
):
@@ -35,21 +33,18 @@ async def test_create_without_id(
3533
await channel.create(random_users[0]["id"])
3634
assert channel.id is not None
3735

38-
@pytest.mark.asyncio
3936
async def test_send_message_with_options(self, channel: Channel, random_user: Dict):
4037
response = await channel.send_message(
4138
{"text": "hi"}, random_user["id"], skip_push=True
4239
)
4340
assert "message" in response
4441
assert response["message"]["text"] == "hi"
4542

46-
@pytest.mark.asyncio
4743
async def test_send_event(self, channel: Channel, random_user: Dict):
4844
response = await channel.send_event({"type": "typing.start"}, random_user["id"])
4945
assert "event" in response
5046
assert response["event"]["type"] == "typing.start"
5147

52-
@pytest.mark.asyncio
5348
async def test_send_reaction(self, channel: Channel, random_user: Dict):
5449
msg = await channel.send_message({"text": "hi"}, random_user["id"])
5550
response = await channel.send_reaction(
@@ -59,7 +54,6 @@ async def test_send_reaction(self, channel: Channel, random_user: Dict):
5954
assert len(response["message"]["latest_reactions"]) == 1
6055
assert response["message"]["latest_reactions"][0]["type"] == "love"
6156

62-
@pytest.mark.asyncio
6357
async def test_delete_reaction(self, channel: Channel, random_user: Dict):
6458
msg = await channel.send_message({"text": "hi"}, random_user["id"])
6559
await channel.send_reaction(
@@ -71,13 +65,11 @@ async def test_delete_reaction(self, channel: Channel, random_user: Dict):
7165
assert "message" in response
7266
assert len(response["message"]["latest_reactions"]) == 0
7367

74-
@pytest.mark.asyncio
7568
async def test_update(self, channel: Channel):
7669
response = await channel.update({"motd": "one apple a day..."})
7770
assert "channel" in response
7871
assert response["channel"]["motd"] == "one apple a day..."
7972

80-
@pytest.mark.asyncio
8173
async def test_update_partial(self, channel: Channel):
8274
response = await channel.update({"color": "blue", "age": 30})
8375
assert "channel" in response
@@ -91,18 +83,15 @@ async def test_update_partial(self, channel: Channel):
9183
assert response["channel"]["color"] == "red"
9284
assert "age" not in response["channel"]
9385

94-
@pytest.mark.asyncio
9586
async def test_delete(self, channel: Channel):
9687
response = await channel.delete()
9788
assert "channel" in response
9889
assert response["channel"].get("deleted_at") is not None
9990

100-
@pytest.mark.asyncio
10191
async def test_truncate(self, channel: Channel):
10292
response = await channel.truncate()
10393
assert "channel" in response
10494

105-
@pytest.mark.asyncio
10695
async def test_truncate_with_options(self, channel: Channel, random_user: Dict):
10796
response = await channel.truncate(
10897
skip_push=True,
@@ -113,7 +102,6 @@ async def test_truncate_with_options(self, channel: Channel, random_user: Dict):
113102
)
114103
assert "channel" in response
115104

116-
@pytest.mark.asyncio
117105
async def test_add_members(self, channel: Channel, random_user: Dict):
118106
response = await channel.remove_members([random_user["id"]])
119107
assert len(response["members"]) == 0
@@ -122,15 +110,13 @@ async def test_add_members(self, channel: Channel, random_user: Dict):
122110
assert len(response["members"]) == 1
123111
assert not response["members"][0].get("is_moderator", False)
124112

125-
@pytest.mark.asyncio
126113
async def test_add_members_with_options(self, channel: Channel, random_user: Dict):
127114
response = await channel.remove_members([random_user["id"]])
128115
assert len(response["members"]) == 0
129116

130117
response = await channel.add_members([random_user["id"]], hide_history=True)
131118
assert len(response["members"]) == 1
132119

133-
@pytest.mark.asyncio
134120
async def test_invite_members(self, channel: Channel, random_user: Dict):
135121
response = await channel.remove_members([random_user["id"]])
136122
assert len(response["members"]) == 0
@@ -139,15 +125,13 @@ async def test_invite_members(self, channel: Channel, random_user: Dict):
139125
assert len(response["members"]) == 1
140126
assert response["members"][0].get("invited", True)
141127

142-
@pytest.mark.asyncio
143128
async def test_add_moderators(self, channel: Channel, random_user: Dict):
144129
response = await channel.add_moderators([random_user["id"]])
145130
assert response["members"][0]["is_moderator"]
146131

147132
response = await channel.demote_moderators([random_user["id"]])
148133
assert not response["members"][0].get("is_moderator", False)
149134

150-
@pytest.mark.asyncio
151135
async def test_assign_roles_moderators(self, channel: Channel, random_user: Dict):
152136
member = {"user_id": random_user["id"], "channel_role": "channel_moderator"}
153137
response = await channel.add_members([member])
@@ -159,13 +143,11 @@ async def test_assign_roles_moderators(self, channel: Channel, random_user: Dict
159143
assert len(response["members"]) == 1
160144
assert response["members"][0]["channel_role"] == "channel_member"
161145

162-
@pytest.mark.asyncio
163146
async def test_mark_read(self, channel: Channel, random_user: Dict):
164147
response = await channel.mark_read(random_user["id"])
165148
assert "event" in response
166149
assert response["event"]["type"] == "message.read"
167150

168-
@pytest.mark.asyncio
169151
async def test_get_replies(self, channel: Channel, random_user: Dict):
170152
msg = await channel.send_message({"text": "hi"}, random_user["id"])
171153
response = await channel.get_replies(msg["message"]["id"])
@@ -187,7 +169,6 @@ async def test_get_replies(self, channel: Channel, random_user: Dict):
187169
assert len(response["messages"]) == 3
188170
assert response["messages"][0]["index"] == 7
189171

190-
@pytest.mark.asyncio
191172
async def test_get_reactions(self, channel: Channel, random_user: Dict):
192173
msg = await channel.send_message({"text": "hi"}, random_user["id"])
193174
response = await channel.get_reactions(msg["message"]["id"])
@@ -211,14 +192,12 @@ async def test_get_reactions(self, channel: Channel, random_user: Dict):
211192

212193
assert response["reactions"][0]["count"] == 42
213194

214-
@pytest.mark.asyncio
215195
async def test_send_and_delete_file(self, channel: Channel, random_user: Dict):
216196
url = "helloworld.jpg"
217197
resp = await channel.send_file(url, "helloworld.jpg", random_user)
218198
assert "helloworld.jpg" in resp["file"]
219199
await channel.delete_file(resp["file"])
220200

221-
@pytest.mark.asyncio
222201
async def test_send_and_delete_image(self, channel: Channel, random_user: Dict):
223202
url = "helloworld.jpg"
224203
resp = await channel.send_image(
@@ -227,7 +206,6 @@ async def test_send_and_delete_image(self, channel: Channel, random_user: Dict):
227206
assert "helloworld.jpg" in resp["file"]
228207
await channel.delete_image(resp["file"])
229208

230-
@pytest.mark.asyncio
231209
async def test_channel_hide_show(
232210
self, client: StreamChatAsync, channel: Channel, random_users: List[Dict]
233211
):
@@ -271,7 +249,6 @@ async def test_channel_hide_show(
271249
)
272250
assert len(response["channels"]) == 1
273251

274-
@pytest.mark.asyncio
275252
async def test_invites(self, client: StreamChatAsync, channel: Channel):
276253
members = ["john", "paul", "george", "pete", "ringo", "eric"]
277254
await client.update_users([{"id": m} for m in members])
@@ -301,7 +278,6 @@ async def test_invites(self, client: StreamChatAsync, channel: Channel):
301278
# can reject again, noop
302279
await channel.reject_invite("eric")
303280

304-
@pytest.mark.asyncio
305281
async def test_query_members(self, client: StreamChatAsync, channel: Channel):
306282
members = ["paul", "george", "john", "jessica", "john2"]
307283
await client.update_users([{"id": m, "name": m} for m in members])
@@ -319,7 +295,6 @@ async def test_query_members(self, client: StreamChatAsync, channel: Channel):
319295
assert response[0]["user"]["id"] == "jessica"
320296
assert response[1]["user"]["id"] == "john2"
321297

322-
@pytest.mark.asyncio
323298
async def test_mute_unmute(
324299
self, client: StreamChatAsync, channel: Channel, random_users: List[Dict]
325300
):
@@ -342,7 +317,6 @@ async def test_mute_unmute(
342317
)
343318
assert len(response["channels"]) == 0
344319

345-
@pytest.mark.asyncio
346320
async def test_export_channel_status(
347321
self, client: StreamChatAsync, channel: Channel
348322
):
@@ -352,7 +326,6 @@ async def test_export_channel_status(
352326
with pytest.raises(StreamAPIException, match=r".*Can't find channel.*"):
353327
await client.export_channel("messaging", str(uuid.uuid4()))
354328

355-
@pytest.mark.asyncio
356329
async def test_export_channel(
357330
self, client: StreamChatAsync, channel: Channel, random_users: List[Dict]
358331
):

0 commit comments

Comments
 (0)