Skip to content

Commit b1130e9

Browse files
Create amb_accepted.yml
1 parent 5265b40 commit b1130e9

File tree

1 file changed

+194
-0
lines changed

1 file changed

+194
-0
lines changed

.github/workflows/amb_accepted.yml

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
name: "Ambassador Accepted: Post + Export"
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
label_name:
7+
description: "Target label"
8+
required: true
9+
default: Accepted
10+
type: string
11+
dry_run:
12+
description: "Preview without posting"
13+
required: true
14+
default: false
15+
type: boolean
16+
17+
permissions:
18+
contents: read
19+
issues: write
20+
21+
jobs:
22+
post-and-export:
23+
runs-on: ubuntu-latest
24+
steps:
25+
- name: Checkout repo
26+
uses: actions/checkout@v4
27+
28+
- name: Set up Python
29+
uses: actions/setup-python@v5
30+
with:
31+
python-version: "3.11"
32+
33+
- name: Install deps
34+
run: pip install requests
35+
36+
- name: Post message and export CSV
37+
env:
38+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
39+
REPO_FULL: ${{ github.repository }}
40+
LABEL_NAME: ${{ inputs.label_name }}
41+
DRY_RUN: ${{ inputs.dry_run }}
42+
run: |
43+
python - <<'PY'
44+
import os, re, csv, sys, requests
45+
from requests.adapters import HTTPAdapter, Retry
46+
47+
token = os.getenv("GITHUB_TOKEN")
48+
repo_full = os.getenv("REPO_FULL")
49+
label_name = os.getenv("LABEL_NAME","Accepted")
50+
dry_run = (os.getenv("DRY_RUN","false").lower()=="true")
51+
52+
if not token or not repo_full:
53+
print("Missing GITHUB_TOKEN or REPO_FULL", file=sys.stderr); sys.exit(1)
54+
owner, repo = repo_full.split("/",1)
55+
56+
s = requests.Session()
57+
s.headers.update({
58+
"Authorization": f"Bearer {token}",
59+
"Accept": "application/vnd.github+json",
60+
"X-GitHub-Api-Version": "2022-11-28"
61+
})
62+
s.mount("https://", HTTPAdapter(max_retries=Retry(
63+
total=8, backoff_factor=0.7,
64+
status_forcelist=[429,500,502,503,504],
65+
allowed_methods=["GET","POST","HEAD","OPTIONS"]
66+
)))
67+
68+
MESSAGE_TEMPLATE = """Hello {first_name}
69+
70+
You’ve been invited to join the private **Ambassador Program Management Repository**:
71+
👉 [pytorch-fdn/ambassador-program-management](https://github.com/pytorch-fdn/ambassador-program-management/)
72+
73+
Once you accept the invitation, you’ll find important information in the discussion thread — including instructions to submit a new issue using the **“Ambassador Confirmation & Details”** template.
74+
75+
To confirm your participation in the 2025 PyTorch Ambassador Program, please open a new issue using the **“Ambassador Confirmation & Details”** template here:
76+
👉 [Create your confirmation issue](https://github.com/pytorch-fdn/ambassador-program-management/issues/new/choose)
77+
78+
_Completing this step will confirm your role and get you ready for onboarding 🚀_
79+
80+
As part of onboarding, we’ll be hosting a **Kick-Off Call** where we’ll share important program details, expectations, and next steps:
81+
82+
📅 **Date:** Monday, September 8
83+
🕒 **Time:** 5:30 PM CEST / 8:30 AM PDT
84+
85+
Once you confirm your role, you will also receive a calendar invite with the meeting link.
86+
87+
We’re truly excited to welcome you into the PyTorch Ambassador Program and look forward to the impact you’ll continue to make in the community.
88+
89+
Best regards,
90+
The PyTorch PMO
91+
"""
92+
93+
# ---------- Helpers ----------
94+
def get_accepted_open_issues():
95+
url = f"https://api.github.com/repos/{owner}/{repo}/issues"
96+
params = {"state":"open", "labels":label_name, "per_page":100}
97+
while url:
98+
r = s.get(url, params=params if 'params' in locals() else None)
99+
if r.status_code != 200:
100+
print(f"Failed to list issues: {r.status_code} {r.text}", file=sys.stderr); sys.exit(1)
101+
for it in r.json():
102+
if "pull_request" not in it:
103+
yield it
104+
url = r.links.get("next",{}).get("url")
105+
params = None
106+
107+
def parse_after_label(body:str, label:str):
108+
"""
109+
Finds the line immediately following a label header block:
110+
Label
111+
value
112+
Returns '' if not found.
113+
"""
114+
pat = rf"{re.escape(label)}\s*\r?\n([^\n\r]+)"
115+
m = re.search(pat, body, re.IGNORECASE)
116+
return m.group(1).strip() if m else ""
117+
118+
def extract_fields(issue):
119+
body = issue.get("body") or ""
120+
nominee_name = parse_after_label(body, "Nominee Name")
121+
nominee_email = parse_after_label(body, "Nominee Email")
122+
location = parse_after_label(body, "City, State/Province, Country")
123+
gh_handle = parse_after_label(body, "Nominee's GitHub or GitLab Handle")
124+
125+
# Normalize handle: if it's a URL like https://github.com/user -> take last path segment
126+
if gh_handle.startswith("http"):
127+
m = re.search(r"github\.com/([^/\s]+)", gh_handle, re.IGNORECASE)
128+
if m:
129+
gh_handle = m.group(1)
130+
131+
# First name: from nominee_name first token; fallback to profile name; then login
132+
first_name = ""
133+
if nominee_name:
134+
first_name = nominee_name.split()[0].strip()
135+
if not first_name:
136+
user = issue.get("user") or {}
137+
prof_name = (user.get("name") or "").strip()
138+
if prof_name:
139+
first_name = prof_name.split()[0]
140+
else:
141+
first_name = (user.get("login") or "there")
142+
143+
return {
144+
"issue_number": issue["number"],
145+
"first_name": first_name,
146+
"nominee_name": nominee_name,
147+
"nominee_email": nominee_email,
148+
"location": location,
149+
"github_handle": gh_handle
150+
}
151+
152+
def post_comment(issue_number, body):
153+
if dry_run:
154+
print(f"DRY-RUN: would post to #{issue_number}:\n{body[:300]}...\n---")
155+
return
156+
r = s.post(
157+
f"https://api.github.com/repos/{owner}/{repo}/issues/{issue_number}/comments",
158+
json={"body": body}
159+
)
160+
if r.status_code not in (200,201):
161+
print(f"Failed to comment on #{issue_number}: {r.status_code} {r.text}", file=sys.stderr)
162+
163+
# ---------- Process ----------
164+
rows = []
165+
count = 0
166+
for issue in get_accepted_open_issues():
167+
data = extract_fields(issue)
168+
msg = MESSAGE_TEMPLATE.format(first_name=data["first_name"])
169+
post_comment(data["issue_number"], msg)
170+
count += 1
171+
rows.append([
172+
data["issue_number"],
173+
data["nominee_name"],
174+
data["nominee_email"],
175+
data["location"],
176+
data["github_handle"]
177+
])
178+
179+
# Export CSV
180+
out = "accepted_export.csv"
181+
with open(out, "w", newline="", encoding="utf-8") as f:
182+
w = csv.writer(f)
183+
w.writerow(["Issue", "Nominee Name", "Nominee Email", "Location", "GitHub Handle"])
184+
w.writerows(rows)
185+
186+
print(f"Posted to {count} accepted issue(s).")
187+
print(f"Exported {len(rows)} row(s) -> {out}")
188+
PY
189+
190+
- name: Upload export artifact
191+
uses: actions/upload-artifact@v4
192+
with:
193+
name: accepted_export
194+
path: accepted_export.csv

0 commit comments

Comments
 (0)