Browse Source

chore: add twitter/bluesky/githubDiscussion release notification (#454)

pull/458/head
Pier CeccoPierangioliEugenio 6 months ago committed by GitHub
parent
commit
4acfedd3f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 28
      .github/workflows/notify-social.yml
  2. 22
      .github/workflows/release.yml
  3. 135
      tools/ci/ci_tools/social/notify_bluesky.py
  4. 70
      tools/ci/ci_tools/social/notify_github_discussion.py
  5. 14
      tools/ci/ci_tools/social/notify_twitter.py
  6. 7
      tools/ci/pyproject.toml
  7. 12
      tools/run.scratchpad.sh

28
.github/workflows/notify-social.yml

@ -18,6 +18,10 @@ on:
description: The release message
type: string
default: pyTermTk released
github-discussion-message:
description: The release message
type: string
default: pyTermTk released
workflow_call:
inputs:
app:
@ -32,10 +36,14 @@ on:
description: The release message
type: string
default: pyTermTk released
github-discussion-message:
description: The release message
type: string
default: pyTermTk released
jobs:
notify-discord:
# runs-on: ubuntu-latest
name: Notify Discord
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
@ -54,5 +62,23 @@ jobs:
run: |
python tools/ci/social/notify_discord.py ${{ inputs.app }} ${{ inputs.version }}
notify-github-discussion:
name: Notify Github Discussion
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.sha }}
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Deploy Github Discussion
env:
DISCUSSION_BODY: ${{ inputs.github-discussion-message}}
GH_DISCUSSION_TOKEN: ${{ secrets.GH_PAT_TOKEN }}
run: |
export DISCUSSION_TITLE="${{ inputs.app }} ${{ inputs.version }} Released!!!"
python tools/ci/social/notify_github_discussion.py ${{ inputs.app }} ${{ inputs.version }}

22
.github/workflows/release.yml

@ -285,3 +285,25 @@ jobs:
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
run: |
notify-discord ${{ matrix.name }} v${{ matrix.version }}
- name: Notify ${{ matrix.name }} on Github Discussion
env:
RN: ${{ matrix.release-notes }}
MESSAGE: ${{ matrix.release-notes }}
GITHUB_TOKEN: ${{ secrets.GH_PAT_TOKEN }}
GH_DISCUSSION_TOKEN: ${{ secrets.GH_DISCUSSION_TOKEN }}
run: |
notify-gh-discussion ${{ matrix.name }} v${{ matrix.version }}
- name: Notify ${{ matrix.name }} on Bluesky
env:
BLUESKY_APP_PWD: ${{ secrets.BLUESKY_APP_PWD }}
BLUESKY_APP_IDENTIFIER: ${{ secrets.BLUESKY_APP_IDENTIFIER }}
run: |
notify-bluesky ${{ matrix.name }} v${{ matrix.version }}
- name: Notify ${{ matrix.name }} on Twitter
env:
X_API_KEY: ${{ secrets.X_API_KEY }}
X_API_SECRET: ${{ secrets.X_API_SECRET }}
X_ACCESS_TOKEN: ${{ secrets.X_ACCESS_TOKEN }}
X_ACCESS_TOKEN_SECRET: ${{ secrets.X_ACCESS_TOKEN_SECRET }}
run: |
notify-twitter ${{ matrix.name }} ${{ matrix.version }}

135
tools/ci/ci_tools/social/notify_bluesky.py

@ -23,53 +23,114 @@
import os,sys
import requests
import argparse
from datetime import datetime
from pprint import pprint
from typing import Dict
current_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(current_dir)
from social_common import get_social_data, SocialData, get_env_var
identifier=get_env_var('BLUESKY_APP_IDENTIFIER')
password=get_env_var('BLUESKY_APP_PWD')
import re
from typing import List, Dict
def _get_facet(txt:str, slice:str, content:Dict ) -> Dict:
start = txt.index(slice)
end = start + len(slice)
return {
"index": { "byteStart": start, "byteEnd": end },
"features": [ content ]
}
def main():
parser = argparse.ArgumentParser(description="Send a Discord notification.")
parser.add_argument("app", type=str, help="The application name.")
parser.add_argument("version", type=str, help="The application version.")
args = parser.parse_args()
data = get_social_data(args.app)
if not data:
raise ValueError(f"app: {args.app} is not recognised")
identifier=get_env_var('BLUESKY_APP_IDENTIFIER')
password=get_env_var('BLUESKY_APP_PWD')
# Step 1: Authenticate and get access token
auth_response = requests.post(
"https://bsky.social/xrpc/com.atproto.server.createSession",
# Step 1: Authenticate and get access token
auth_response = requests.post(
"https://bsky.social/xrpc/com.atproto.server.createSession",
headers = {
"Content-Type": "application/json"
},
json={
"identifier": identifier,
"password": password
}
)
auth_data = auth_response.json()
# print(auth_data)
access_token = auth_data["accessJwt"]
did = auth_data["did"]
print(f"::add-mask::{did}")
# Step 2: Post a message
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
},
json={
"identifier": identifier,
"password": password
}
)
auth_data = auth_response.json()
# print(auth_data)
access_token = auth_data["accessJwt"]
did = auth_data["did"]
# Step 2: Post a message
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
post_data = {
"repo": did,
"collection": "app.bsky.feed.post",
"record": {
"$type": "app.bsky.feed.post",
"text": "Hello from my Python bot!",
"createdAt": datetime.now().isoformat() + "Z"
text = f"{args.app} v{args.version} Released\n\n{data.link}\n\n#pyTermTk #TUI #Python #Linux #terminal"
post_data = {
"repo": did,
"collection": "app.bsky.feed.post",
"record": {
"$type": "app.bsky.feed.post",
"text":text,
"facets": [
_get_facet(
text, data.link,
{ "uri": data.link , "$type": "app.bsky.richtext.facet#link" }
),
_get_facet(
text, '#pyTermTk',
{ "tag": 'pyTermTk' , "$type": "app.bsky.richtext.facet#tag" }
),
_get_facet(
text, '#TUI',
{ "tag": 'TUI' , "$type": "app.bsky.richtext.facet#tag" }
),
_get_facet(
text, '#Python',
{ "tag": 'Python' , "$type": "app.bsky.richtext.facet#tag" }
),
_get_facet(
text, '#Linux',
{ "tag": 'Linux' , "$type": "app.bsky.richtext.facet#tag" }
),
_get_facet(
text, '#terminal',
{ "tag": 'terminal' , "$type": "app.bsky.richtext.facet#tag" }
),
],
"createdAt": datetime.now().isoformat() + "Z"
}
}
}
post_response = requests.post(
"https://bsky.social/xrpc/com.atproto.repo.createRecord",
headers=headers,
json=post_data
)
print('::group::Data')
pprint(post_data)
print('::endgroup::')
post_response = requests.post(
"https://bsky.social/xrpc/com.atproto.repo.createRecord",
headers=headers,
json=post_data
)
print(post_response.status_code)
print(post_response.json())
print(post_response.status_code)
print(post_response.json())
if __name__ == "__main__":
main()

70
tools/ci/ci_tools/social/notify_github_discussion.py

@ -23,29 +23,21 @@
import os, sys
import requests
import argparse
current_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(current_dir)
from social_common import get_social_data, SocialData, get_env_var
# === CONFIGURATION ===
GITHUB_TOKEN = get_env_var('GH_DISCUSSION_TOKEN')
REPO_OWNER = "ceccopierangiolieugenio"
REPO_NAME = "pyTermTk"
DICSUSSION_CATEGORY="announcements"
DISCUSSION_TITLE = "Your Announcement Title"
DISCUSSION_BODY = "This is the content of your announcement."
# === FUNCTIONS ===
def get_repo_id(owner, repo, token):
query = f"""
query {{
repository(owner: "{owner}", name: "{repo}") {{
id
query {{
repository(owner: "{owner}", name: "{repo}") {{
id
}}
}}
}}
"""
headers = {
"Authorization": f"Bearer {token}",
@ -54,7 +46,7 @@ def get_repo_id(owner, repo, token):
response = requests.post('https://api.github.com/graphql', json={'query': query}, headers=headers)
return response.json()['data']['repository']['id']
def get_category_id(repo_id, token):
def get_category_id(repo_id, token, discussion_category):
query = f"""
query {{
node(id: "{repo_id}") {{
@ -76,7 +68,7 @@ def get_category_id(repo_id, token):
response = requests.post('https://api.github.com/graphql', json={'query': query}, headers=headers)
categories = response.json()['data']['node']['discussionCategories']['nodes']
for category in categories:
if category['name'].lower() == DICSUSSION_CATEGORY:
if category['name'].lower() == discussion_category:
return category['id']
raise Exception("Announcements category not found")
@ -107,13 +99,41 @@ def create_discussion(repo_id, category_id, title, body, token):
# === MAIN EXECUTION ===
try:
repo_id = get_repo_id(REPO_OWNER, REPO_NAME, GITHUB_TOKEN)
print(f"{repo_id=}")
category_id = get_category_id(repo_id, GITHUB_TOKEN)
print(f"{category_id=}")
discussion_url = create_discussion(repo_id, category_id, DISCUSSION_TITLE, DISCUSSION_BODY, GITHUB_TOKEN)
print(f"{discussion_url=}")
print(f"✅ Discussion created successfully: {discussion_url}")
except Exception as e:
print(f"❌ Error: {e}")
def main():
parser = argparse.ArgumentParser(description="Send a Discord notification.")
parser.add_argument("app", type=str, help="The application name.")
parser.add_argument("version", type=str, help="The application version.")
args = parser.parse_args()
# === CONFIGURATION ===
GITHUB_TOKEN = get_env_var('GITHUB_TOKEN')
GH_DISCUSSION_TOKEN = get_env_var('GH_DISCUSSION_TOKEN')
REPO_OWNER = "ceccopierangiolieugenio"
REPO_NAME = "pyTermTk"
DICSUSSION_CATEGORY="announcements"
DISCUSSION_BODY = get_env_var('RN')
data = get_social_data(args.app)
if not data:
raise ValueError(f"app: {args.app} is not recognised")
try:
repo_id = get_repo_id(REPO_OWNER, REPO_NAME, GITHUB_TOKEN)
print(f"{repo_id=}")
category_id = get_category_id(repo_id, GITHUB_TOKEN, DICSUSSION_CATEGORY)
print(f"{category_id=}")
discussion_url = create_discussion(
repo_id, category_id,
f"{args.app} {args.version} Released!!!",
DISCUSSION_BODY, GH_DISCUSSION_TOKEN)
print(f"{discussion_url=}")
print(f"✅ Discussion created successfully: {discussion_url}")
except Exception as e:
print(f"❌ Error: {e}")
if __name__ == "__main__":
main()

14
tools/ci/ci_tools/social/notify_twitter.py

@ -32,23 +32,21 @@ current_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(current_dir)
from social_common import get_social_data, SocialData, get_env_var
def send_twitter_message(version: str, data:SocialData):
def send_twitter_message(version: str, data:SocialData, text:str):
api_key = get_env_var("X_API_KEY")
api_secret = get_env_var("X_API_SECRET")
access_token = get_env_var("X_ACCESS_TOKEN")
access_token_secret = get_env_var("X_ACCESS_TOKEN_SECRET")
message = get_env_var("MESSAGE")
twitter = OAuth1Session(api_key, api_secret, access_token, access_token_secret)
payload = {"text": "Hello from OAuth 1.0a!"}
payload = {"text": text}
response = twitter.post("https://api.twitter.com/2/tweets", json=payload)
print(response.status_code)
print(response.json())
if __name__ == "__main__":
def main():
parser = argparse.ArgumentParser(description="Send a Twitter/X notification.")
parser.add_argument("app", type=str, help="The application name.")
parser.add_argument("version", type=str, help="The application version.")
@ -58,4 +56,8 @@ if __name__ == "__main__":
if not data:
raise ValueError(f"app: {args.app} is not recognised")
send_twitter_message(args.version, data)
text = f"{args.app} v{args.version} Released\n\n{data.link}\n\n#pyTermTk #TUI #Python #Linux #terminal"
send_twitter_message(args.version, data, text)
if __name__ == "__main__":
main()

7
tools/ci/pyproject.toml

@ -16,12 +16,17 @@
[project.optional-dependencies]
social = [
'discord.py==2.5.2'
'discord.py==2.5.2',
'requests==2.32.5',
'requests_oauthlib==2.0.0'
]
[project.scripts]
release-helper = "ci_tools.release_helper:main"
notify-discord = "ci_tools.social.notify_discord:main"
notify-bluesky = "ci_tools.social.notify_bluesky:main"
notify-twitter = "ci_tools.social.notify_twitter:main"
notify-gh-discussion = "ci_tools.social.notify_github_discussion:main"
[tool.setuptools]
packages = ["ci_tools"]

12
tools/run.scratchpad.sh

@ -0,0 +1,12 @@
REPO="ceccopierangiolieugenio/pyTermTk"
WORKFLOW="_scratchpad.yml"
REF=$(git branch --show-current)
WORKFLOW_URL="https://github.com/$REPO/actions/workflows/$WORKFLOW"
curl -s -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
"https://api.github.com/repos/$REPO/actions/workflows/$WORKFLOW/dispatches" \
-d "{\"ref\":\"$REF\"}"
echo "Scratchpad workflow triggered from branch $REF. You can monitor the progress here: $WORKFLOW_URL"
Loading…
Cancel
Save