Skip to content

Commit aec20c5

Browse files
authored
1.2.0 (#1)
* ## 1.2.0 * Add new ``github.get_contents`` action which allows user to retrieve file and repository contents. * Add new ``github.create_file`` action which allows user to create new files in repositories. * Add new ``github.update_file`` action which allows user to update existing files in repositories. * Bump libs version in requirements.txt * fix typo * token/password must be secret in pack's config * fixing config schema's default token reference to datastore * changelog update * config schema update
1 parent 4774896 commit aec20c5

15 files changed

+345
-45
lines changed

CHANGES.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog
22

3+
## 1.2.0
4+
5+
__IMPORTANT__: Configuration scheme changed to mark token and password as secret, if you were using st2 datastore, you might need to encrypt the values!
6+
7+
* Add new ``github.get_contents`` action which allows user to retrieve file and repository contents.
8+
* Add new ``github.create_file`` action which allows user to create new files in repositories.
9+
* Add new ``github.update_file`` action which allows user to update existing files in repositories.
10+
* Bump libs version in requirements.txt
11+
312
## 1.1.0
413

514
* Add new ``github.get_pull`` action which allows user to retrieve details about

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ StackStorm webhook handler.
107107
* ``list_pulls`` - List all pull requests for a particular repo.
108108
* ``review_pull`` - Create a review for the provided pull request.
109109
* ``merge_pull`` - Merge the provided pull request.
110+
* ``get_contents`` - Get repository or file contents.
111+
* ``create_file`` - Create new file.
112+
* ``update_file`` - Update existing file.
110113

111114
## Rules
112115

actions/create_file.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from lib.base import BaseGithubAction
2+
from lib.formatters import file_response_to_dict
3+
from github.GithubObject import NotSet
4+
5+
__all__ = [
6+
'CreateFileAction'
7+
]
8+
9+
10+
class CreateFileAction(BaseGithubAction):
11+
def run(self, user, repo, path, message, content, branch=None, committer=None, author=None):
12+
if not branch:
13+
branch = NotSet
14+
if not committer:
15+
committer = NotSet
16+
if not author:
17+
author = NotSet
18+
19+
user = self._client.get_user(user)
20+
repo = user.get_repo(repo)
21+
api_response = repo.create_file(path=path, message=message, content=content, branch=branch,
22+
committer=committer, author=author)
23+
result = file_response_to_dict(api_response)
24+
return result
25+
26+
27+
if __name__ == '__main__':
28+
import os
29+
GITHUB_TOKEN = os.environ.get('GITHUB_TOKEN')
30+
GITHUB_ORG = os.environ.get('GITHUB_ORG')
31+
GITHUB_REPO = os.environ.get('GITHUB_REPO')
32+
33+
act = CreateFileAction(config={'token': GITHUB_TOKEN, 'github_type': 'online'})
34+
res = act.run(user=GITHUB_ORG, repo=GITHUB_REPO, path='README5.md', message='Test commit',
35+
content='Super duper read me file, pushed from Stackstorm github pack!\n',
36+
branch='branch1')
37+
import pprint
38+
pp = pprint.PrettyPrinter(indent=4)
39+
pp.pprint(res)

actions/create_file.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
name: create_file
3+
runner_type: python-script
4+
description: Create a file in this repository.
5+
enabled: true
6+
entry_point: create_file.py
7+
parameters:
8+
user:
9+
type: "string"
10+
description: "User / organization name."
11+
required: true
12+
repo:
13+
type: "string"
14+
description: "Repository name."
15+
required: true
16+
path:
17+
type: "string"
18+
description: "Path of the file in the repository."
19+
required: true
20+
message:
21+
type: "string"
22+
description: "Commit message."
23+
required: true
24+
content:
25+
type: "string"
26+
description: "The actual data in the file."
27+
required: true
28+
branch:
29+
type: "string"
30+
description: "Branch to create the commit on. Defaults to the default branch of the repository"
31+
required: false
32+
committer:
33+
type: "string"
34+
description: "If no information is given the authenticated user's information will be used. You must specify both a name and email."
35+
required: false
36+
author:
37+
type: "string"
38+
description: "If omitted this will be filled in with committer information. If passed, you must specify both a name and email."
39+
required: false

actions/get_contents.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from lib.base import BaseGithubAction
2+
from lib.formatters import contents_to_dict
3+
4+
__all__ = [
5+
'GetContentsAction'
6+
]
7+
8+
9+
class GetContentsAction(BaseGithubAction):
10+
def run(self, user, repo, ref, path, decode=False):
11+
12+
user = self._client.get_user(user)
13+
repo = user.get_repo(repo)
14+
contents = repo.get_contents(path, ref=ref)
15+
result = contents_to_dict(contents=contents, decode=decode)
16+
return result
17+
18+
19+
if __name__ == '__main__':
20+
import os
21+
GITHUB_TOKEN = os.environ.get('GITHUB_TOKEN')
22+
GITHUB_ORG = os.environ.get('GITHUB_ORG')
23+
GITHUB_REPO = os.environ.get('GITHUB_REPO')
24+
25+
act = GetContentsAction(config={'token': GITHUB_TOKEN, 'github_type': 'online'})
26+
res = act.run(user=GITHUB_ORG, repo=GITHUB_REPO, ref='branch1', path='README.md', decode=True)
27+
import pprint
28+
pp = pprint.PrettyPrinter(indent=4)
29+
pp.pprint(res)

actions/get_contents.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
name: get_contents
3+
runner_type: python-script
4+
description: Gets the contents of a file or directory in a repository.
5+
enabled: true
6+
entry_point: get_contents.py
7+
parameters:
8+
user:
9+
type: "string"
10+
description: "User / organization name."
11+
required: true
12+
repo:
13+
type: "string"
14+
description: "Repository name."
15+
required: true
16+
ref:
17+
type: "string"
18+
description: "Git ref."
19+
required: false
20+
default: "HEAD"
21+
path:
22+
type: "string"
23+
description: "Path to file."
24+
required: true
25+
decode:
26+
type: boolean
27+
description: "Decode content."
28+
required: false
29+
default: false

actions/lib/base.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919

2020
class BaseGithubAction(Action):
21+
def run(self, **kwargs):
22+
pass
23+
2124
def __init__(self, config):
2225
super(BaseGithubAction, self).__init__(config=config)
2326
token = self.config.get('token', None)
@@ -36,7 +39,7 @@ def __init__(self, config):
3639
self._session = requests.Session()
3740

3841
def _web_session(self, web_url=DEFAULT_WEB_URL):
39-
'''Returns a requests session to scrape off the web'''
42+
"""Returns a requests session to scrape off the web"""
4043
login_url = web_url + '/login'
4144
session = requests.Session()
4245
request = session.get(login_url).text
@@ -94,7 +97,7 @@ def _get_user_token(self, user, enterprise):
9497
token = self.action_service.get_value(token_name + user)
9598

9699
# if a token is not returned, try using reversing changes made by
97-
# GitHub Enterpise during LDAP sync'ing.
100+
# GitHub Enterprise during LDAP sync'ing.
98101
if token is None:
99102
token = self.action_service.get_value(
100103
token_name + user.replace("-", "."))
@@ -119,6 +122,7 @@ def _request(self, method, uri, payload, token, enterprise):
119122
else:
120123
url = "{}{}".format(DEFAULT_API_URL, uri)
121124

125+
r = None
122126
try:
123127
r = self._session.request(method,
124128
url,

actions/lib/formatters.py

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
'pull_to_dict',
88
'commit_to_dict',
99
'label_to_dict',
10-
'user_to_dict'
10+
'user_to_dict',
11+
'contents_to_dict',
12+
'file_response_to_dict'
1113
]
1214

1315

@@ -75,6 +77,7 @@ def pull_to_dict(pull):
7577
result['head'] = pull.head.ref
7678
result['state'] = pull.state
7779
result['merged'] = pull.merged
80+
# noinspection SpellCheckingInspection
7881
result['mergeable_state'] = pull.mergeable_state
7982
result['merge_commit_sha'] = pull.merge_commit_sha
8083

@@ -116,36 +119,93 @@ def pull_to_dict(pull):
116119

117120

118121
def commit_to_dict(commit):
119-
result = {}
120-
result['sha'] = commit.sha
122+
result = {'sha': commit.sha}
121123
return result
122124

123125

124126
def label_to_dict(label):
125-
result = {}
127+
result = {'name': label.name, 'color': label.color, 'url': label.url}
126128

127-
result['name'] = label.name
128-
result['color'] = label.color
129-
result['url'] = label.url
130129
return result
131130

132131

133132
def user_to_dict(user):
134133
if not user:
135134
return None
136135

137-
result = {}
138-
result['name'] = user.name
139-
result['login'] = user.login
136+
result = {'name': user.name, 'login': user.login}
140137
return result
141138

142139

143140
def team_to_dict(team):
144141
if not team:
145142
return None
146143

147-
result = {}
148-
result['id'] = team.id
149-
result['name'] = team.name
150-
result['members_count'] = team.members_count
144+
result = {'id': team.id, 'name': team.name, 'members_count': team.members_count}
145+
return result
146+
147+
148+
def contents_to_dict(contents, decode=False):
149+
if not contents:
150+
return None
151+
152+
directory = False
153+
if isinstance(contents, list):
154+
directory = True
155+
else:
156+
contents = [contents]
157+
158+
result = []
159+
data = {}
160+
161+
for item in contents:
162+
item_type = item.type
163+
data['type'] = item_type
164+
if item_type == 'symlink':
165+
data['target'] = item.target
166+
elif item_type == 'submodule':
167+
data['submodule_git_url'] = item.submodule_git_url
168+
elif not directory:
169+
encoding = item.encoding
170+
content = item.content
171+
data['encoding'] = encoding
172+
if decode and encoding == 'base64':
173+
content = decode_base64(content)
174+
data['content'] = content
175+
176+
data['size'] = item.size
177+
data['name'] = item.name
178+
data['path'] = item.path
179+
data['sha'] = item.sha
180+
data['url'] = item.url
181+
data['git_url'] = item.git_url
182+
data['html_url'] = item.html_url
183+
data['download_url'] = item.download_url
184+
result.append(data)
185+
186+
if not directory:
187+
return result[0]
188+
else:
189+
return result
190+
191+
192+
def decode_base64(data):
193+
"""Decode base64, padding being optional.
194+
195+
:param data: Base64 data as an ASCII byte string
196+
:returns: The decoded byte string.
197+
198+
"""
199+
missing_padding = len(data) % 4
200+
if missing_padding != 0:
201+
data += b'=' * (4 - missing_padding)
202+
203+
import base64
204+
data = data.encode("utf-8")
205+
data = base64.b64decode(data).decode("utf-8")
206+
return data
207+
208+
209+
def file_response_to_dict(response):
210+
result = {'commit': response['commit'].sha}
151211
return result

actions/update_file.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from lib.base import BaseGithubAction
2+
from lib.formatters import file_response_to_dict
3+
from github.GithubObject import NotSet
4+
5+
__all__ = [
6+
'UpdateFileAction'
7+
]
8+
9+
10+
class UpdateFileAction(BaseGithubAction):
11+
def run(self, user, repo, path, message, content, sha, branch=None, committer=None,
12+
author=None):
13+
if not branch:
14+
branch = NotSet
15+
if not committer:
16+
committer = NotSet
17+
if not author:
18+
author = NotSet
19+
20+
user = self._client.get_user(user)
21+
repo = user.get_repo(repo)
22+
api_response = repo.update_file(path=path, message=message, content=content, sha=sha,
23+
branch=branch, committer=committer, author=author)
24+
result = file_response_to_dict(api_response)
25+
return result
26+
27+
28+
if __name__ == '__main__':
29+
import os
30+
GITHUB_TOKEN = os.environ.get('GITHUB_TOKEN')
31+
GITHUB_ORG = os.environ.get('GITHUB_ORG')
32+
GITHUB_REPO = os.environ.get('GITHUB_REPO')
33+
34+
act = UpdateFileAction(config={'token': GITHUB_TOKEN, 'github_type': 'online'})
35+
res = act.run(user=GITHUB_ORG, repo=GITHUB_REPO, path='README.md', message='Test commit',
36+
content='Super duper read me file, pushed from Stackstorm github pack!\n'
37+
'##new lines added!\n\n*YES*\n',
38+
sha='10ddd6d257e01349d514541981aeecea6b2e741d',
39+
branch='branch1')
40+
import pprint
41+
pp = pprint.PrettyPrinter(indent=4)
42+
pp.pprint(res)

0 commit comments

Comments
 (0)