This guide helps you convert your actions/github-script workflows to use github-python-script.
- uses: actions/github-script@v8
with:
script: |
console.log(`Hello ${context.actor}!`)
core.info(`Event: ${context.eventName}`)- uses: austenstone/github-python-script@v1
with:
script: |
print(f"Hello {context.actor}!")
core.info(f"Event: {context.event_name}")JavaScript uses camelCase, Python uses snake_case:
| JavaScript | Python |
|---|---|
context.eventName |
context.event_name |
context.runId |
context.run_id |
context.runNumber |
context.run_number |
JavaScript:
const owner = context.repo.owner;
const repo = context.repo.repo;Python:
owner = context.repo["owner"]
repo = context.repo["repo"]JavaScript:
core.info(`PR #${pr.number}: ${pr.title}`)Python:
core.info(f"PR #{pr.number}: {pr.title}")
# or
core.info("PR #{}: {}".format(pr.number, pr.title))JavaScript:
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'π Thanks for reporting!'
})Python:
github.rest.issues.create_comment(
issue_number=context.issue["number"],
owner=context.repo["owner"],
repo=context.repo["repo"],
body="π Thanks for reporting!"
)JavaScript:
await github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['bug', 'triage']
})Python:
github.rest.issues.add_labels(
issue_number=context.issue["number"],
owner=context.repo["owner"],
repo=context.repo["repo"],
labels=["bug", "triage"]
)JavaScript:
const issues = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open'
})
for (const issue of issues.data) {
console.log(`#${issue.number}: ${issue.title}`)
}Python:
issues = github.rest.issues.list_for_repo(
owner=context.repo["owner"],
repo=context.repo["repo"],
state="open"
)
for issue in issues:
print(f"#{issue.number}: {issue.title}")JavaScript:
const query = `query($owner:String!, $name:String!) {
repository(owner:$owner, name:$name) {
issues(first:10) {
nodes {
number
title
}
}
}
}`;
const variables = {
owner: context.repo.owner,
name: context.repo.repo
}
const result = await github.graphql(query, variables)
console.log(result)Python:
query = """
query($owner:String!, $name:String!) {
repository(owner:$owner, name:$name) {
issues(first:10) {
nodes {
number
title
}
}
}
}
"""
variables = {
"owner": context.repo["owner"],
"name": context.repo["repo"]
}
result = github.graphql(query, variables)
print(result)JavaScript:
const opts = github.rest.issues.listForRepo.endpoint.merge({
...context.repo,
state: 'all'
})
const issues = await github.paginate(opts)Python:
repo = github.get_repo(f"{context.repo['owner']}/{context.repo['repo']}")
issues = github.paginate(repo.get_issues, state="all")JavaScript:
// JavaScript requires async/await
const issue = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: 1
})Python:
# Python calls are synchronous
issue = github.rest.issues.get(
owner=context.repo["owner"],
repo=context.repo["repo"],
issue_number=1
)JavaScript:
const prTitle = context.payload.pull_request.title
const prAuthor = context.payload.pull_request.user.loginPython:
pr_title = context.payload["pull_request"]["title"]
pr_author = context.payload["pull_request"]["user"]["login"]const result = {
status: 'success',
count: 42
}
return result__result__ = {
"status": "success",
"count": 42
}try {
await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: 9999
})
} catch (error) {
core.warning(`Issue not found: ${error.message}`)
}try:
github.rest.issues.get(
owner=context.repo["owner"],
repo=context.repo["repo"],
issue_number=9999
)
except Exception as error:
core.warning(f"Issue not found: {str(error)}")const execa = require('execa')
const { stdout } = await execa('echo', ['hello'])
console.log(stdout)import subprocess
result = subprocess.run(['echo', 'hello'], capture_output=True, text=True)
print(result.stdout)name: Welcome Contributors
on: pull_request_target
jobs:
welcome:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v8
with:
script: |
const creator = context.payload.sender.login
const opts = github.rest.issues.listForRepo.endpoint.merge({
...context.repo,
creator,
state: 'all'
})
const issues = await github.paginate(opts)
for (const issue of issues) {
if (issue.number === context.issue.number) continue
if (issue.pull_request) return
}
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '**Welcome**, new contributor! π'
})name: Welcome Contributors
on: pull_request_target
jobs:
welcome:
runs-on: ubuntu-latest
steps:
- uses: austenstone/github-python-script@v1
with:
script: |
creator = context.payload["sender"]["login"]
repo = github.get_repo(f"{context.repo['owner']}/{context.repo['repo']}")
issues = repo.get_issues(creator=creator, state="all")
for issue in issues:
if issue.number == context.issue["number"]:
continue
if issue.pull_request:
exit()
github.rest.issues.create_comment(
issue_number=context.issue["number"],
owner=context.repo["owner"],
repo=context.repo["repo"],
body="**Welcome**, new contributor! π"
)| Feature | JavaScript | Python |
|---|---|---|
| Variables | const, let, var |
No keyword needed |
| String formatting | `Hello ${name}` |
f"Hello {name}" |
| Array/List | [1, 2, 3] |
[1, 2, 3] |
| Object/Dict | {key: value} |
{"key": value} |
| Comments | // comment or /* */ |
# comment |
console.log() |
print() |
|
| Async calls | await keyword |
Not needed |
| Property access | obj.property |
obj["property"] or obj.property |
| Function calls | camelCase() |
snake_case() |
| Boolean | true, false |
True, False |
| Null | null |
None |
- Test incrementally: Convert one workflow at a time
- Check property names: Remember camelCase β snake_case
- Dictionary access: Use
[]notation for context objects - Remove await: Python calls are synchronous
- Function names: GitHub API methods use snake_case in Python
- Return values: Use
__result__variable instead ofreturn - Imports: Python has built-in modules - no need to
require()
- Check the README for more examples
- Review EXAMPLES.md for advanced patterns
- Open an issue if you encounter problems during migration