Skip to content

Commit 8aed0ef

Browse files
authored
Merge pull request #21 from vsoch/add/action-updater
add updater for action.yml files
2 parents f922984 + acece2b commit 8aed0ef

File tree

9 files changed

+168
-129
lines changed

9 files changed

+168
-129
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and **Merged pull requests**. Critical items to know are:
1414
The versions coincide with releases on pip. Only major versions will be released as tags on Github.
1515

1616
## [0.0.x](https://github.com/rse-ops/actions-updater/tree/main) (0.0.x)
17+
- add support for updating action.yml (with composite action) (0.0.15)
1718
- add line_length parameter to settings (0.0.14)
1819
- enforce fallback to use major versions still enforces commit (0.0.13)
1920
- bug fix to version updater (0.0.12)

action_updater/main/action.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,21 @@ def __init__(self, filename):
2828
def jobs(self):
2929
return self.changes.get("jobs")
3030

31+
@property
32+
def runs(self):
33+
return self.changes.get("runs")
34+
35+
@property
36+
def steps(self):
37+
if self.jobs:
38+
for _, job in self.jobs.items():
39+
for step in job.get("steps", []):
40+
yield step
41+
42+
elif self.runs:
43+
for step in self.runs.get("steps", []):
44+
yield step
45+
3146
def write(self, path, line_length=None):
3247
"""
3348
Save the action to file.

action_updater/main/schemas.py

Lines changed: 78 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,54 @@
88

99
schema_url = "http://json-schema.org/draft-07/schema"
1010

11+
steps = {
12+
"description": "A job contains a sequence of tasks called steps. Steps can run commands, run setup tasks, or run an action in your repository, a public repository, or an action published in a Docker registry. Not all steps run actions, but all actions are run as a step. Each step runs in its own process in the virtual environment and has access to the workspace and filesystem. Because steps are run in their own process, changes to environment variables are not preserved between steps. GitHub provides built-in steps to set up and complete a job.",
13+
"type": "array",
14+
"items": {
15+
"type": "object",
16+
"properties": {
17+
"name": {
18+
"description": "A name for your step to display on GitHub.",
19+
"type": "string",
20+
},
21+
"uses": {
22+
"description": "Selects an action to run as part of a step in your job. An action is a reusable unit of code. You can use an action defined in the same repository as the workflow, a public repository, or in a published Docker container image.",
23+
"type": "string",
24+
},
25+
"with": {
26+
"type": "object",
27+
"description": "A map of the input parameters defined by the action. Each input parameter is a key/value pair. Input parameters are set as environment variables. The variable is prefixed with INPUT_ and converted to upper case.",
28+
"addtionalProperties": {"type": "string"},
29+
},
30+
"env": {
31+
"type": "object",
32+
"description": "Sets environment variables for steps to use in the virtual environment. Public actions may specify expected environment variables in the README file. If you are setting a secret in an environment variable, you must set secrets using the secrets context.",
33+
"addtionalProperties": {"type": "string"},
34+
},
35+
"if": {
36+
"description": "Identifies any steps that must complete successfully before this step will run. It can be a string or an array of strings. If a step fails, all steps that need it will also fail unless the steps use a conditional statement that causes the step to continue.",
37+
"type": "string",
38+
},
39+
"run": {
40+
"description": "Runs command line programs using the operating system's shell. If you do not provide a name, the step name will default to the run command. Commands run using non-login shells by default.",
41+
"type": "string",
42+
},
43+
"working-directory": {
44+
"description": "The default directory that the action uses in a job's workspace.",
45+
"type": "string",
46+
},
47+
"continue-on-error": {
48+
"description": "Prevents a job from failing when a step fails. Set to true to allow a job to pass when this step fails.",
49+
"type": "boolean",
50+
},
51+
"timeout-minutes": {
52+
"description": "The maximum number of minutes to let a workflow run before GitHub automatically cancels it.",
53+
"type": "number",
54+
},
55+
},
56+
},
57+
}
58+
1159
workflow_schema = {
1260
"title": "Github workflow file - https://help.github.com/en/articles/workflow-syntax-for-github-actions",
1361
"$schema": schema_url,
@@ -77,62 +125,18 @@
77125
"default": "ubuntu-latest",
78126
"enum": [
79127
"ubuntu-latest",
128+
"ubuntu-22.04",
129+
"ubuntu-20.04",
80130
"ubuntu-18.04",
81-
"ubuntu-16.04",
82131
"windows-latest",
83132
"windows-2019",
84-
"windows-2016",
85-
"macOS-latest",
86-
"macOS-10.14",
133+
"windows-2022",
134+
"macos-latest",
135+
"macos-12",
136+
"macos-11",
87137
],
88138
},
89-
"steps": {
90-
"description": "A job contains a sequence of tasks called steps. Steps can run commands, run setup tasks, or run an action in your repository, a public repository, or an action published in a Docker registry. Not all steps run actions, but all actions are run as a step. Each step runs in its own process in the virtual environment and has access to the workspace and filesystem. Because steps are run in their own process, changes to environment variables are not preserved between steps. GitHub provides built-in steps to set up and complete a job.",
91-
"type": "array",
92-
"items": {
93-
"type": "object",
94-
"properties": {
95-
"name": {
96-
"description": "A name for your step to display on GitHub.",
97-
"type": "string",
98-
},
99-
"uses": {
100-
"description": "Selects an action to run as part of a step in your job. An action is a reusable unit of code. You can use an action defined in the same repository as the workflow, a public repository, or in a published Docker container image.",
101-
"type": "string",
102-
},
103-
"with": {
104-
"type": "object",
105-
"description": "A map of the input parameters defined by the action. Each input parameter is a key/value pair. Input parameters are set as environment variables. The variable is prefixed with INPUT_ and converted to upper case.",
106-
"addtionalProperties": {"type": "string"},
107-
},
108-
"env": {
109-
"type": "object",
110-
"description": "Sets environment variables for steps to use in the virtual environment. Public actions may specify expected environment variables in the README file. If you are setting a secret in an environment variable, you must set secrets using the secrets context.",
111-
"addtionalProperties": {"type": "string"},
112-
},
113-
"if": {
114-
"description": "Identifies any steps that must complete successfully before this step will run. It can be a string or an array of strings. If a step fails, all steps that need it will also fail unless the steps use a conditional statement that causes the step to continue.",
115-
"type": "string",
116-
},
117-
"run": {
118-
"description": "Runs command line programs using the operating system's shell. If you do not provide a name, the step name will default to the run command. Commands run using non-login shells by default.",
119-
"type": "string",
120-
},
121-
"working-directory": {
122-
"description": "The default directory that the action uses in a job's workspace.",
123-
"type": "string",
124-
},
125-
"continue-on-error": {
126-
"description": "Prevents a job from failing when a step fails. Set to true to allow a job to pass when this step fails.",
127-
"type": "boolean",
128-
},
129-
"timeout-minutes": {
130-
"description": "The maximum number of minutes to let a workflow run before GitHub automatically cancels it.",
131-
"type": "number",
132-
},
133-
},
134-
},
135-
},
139+
"steps": steps,
136140
},
137141
},
138142
},
@@ -164,6 +168,29 @@
164168
"code_theme": {"type": "string", "choices": list(get_all_styles())},
165169
}
166170

171+
action = {
172+
"$schema": schema_url,
173+
"title": "action.yml Schema",
174+
"type": "object",
175+
"required": [
176+
"name",
177+
"description",
178+
"runs",
179+
],
180+
"properties": {
181+
"name": {"type": "string"},
182+
"description": {"type": "string"},
183+
"runs": {
184+
"type": "object",
185+
"properties": {
186+
"using": {"type": "string"},
187+
"steps": steps,
188+
},
189+
},
190+
},
191+
"additionalProperties": True,
192+
}
193+
167194
settings = {
168195
"$schema": schema_url,
169196
"title": "Settings Schema",

action_updater/main/updaters/savestate/update.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,23 @@ def detect(self, action):
1919
self.count = 0
2020

2121
# No point if we don't have jobs!
22-
if not action.jobs:
22+
if not action.steps:
2323
return False
2424

2525
# For each job, look for steps->updater versions
26-
for _, job in action.jobs.items():
27-
for step in job.get("steps", []):
26+
for step in action.steps:
2827

29-
# We are primarily interested in uses
30-
if "run" not in step:
31-
continue
28+
# We are primarily interested in uses
29+
if "run" not in step:
30+
continue
3231

33-
# Update step run lines
34-
updated_lines = update_lines(step["run"], "save-state", "$GITHUB_STATE")
32+
# Update step run lines
33+
updated_lines = update_lines(step["run"], "save-state", "$GITHUB_STATE")
3534

36-
# Keep track of change counts
37-
if updated_lines != step["run"]:
38-
self.count += 1
35+
# Keep track of change counts
36+
if updated_lines != step["run"]:
37+
self.count += 1
3938

40-
step["run"] = updated_lines
39+
step["run"] = updated_lines
4140

4241
return self.count != 0

action_updater/main/updaters/setenv/update.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,22 @@ def detect(self, action):
1919
self.count = 0
2020

2121
# No point if we don't have jobs!
22-
if not action.jobs:
22+
if not action.steps:
2323
return False
2424

2525
# For each job, look for steps->updater versions
26-
for _, job in action.jobs.items():
27-
for step in job.get("steps", []):
26+
for step in action.steps:
2827

29-
# We are primarily interested in uses
30-
if "run" not in step:
31-
continue
28+
# We are primarily interested in uses
29+
if "run" not in step:
30+
continue
3231

33-
# Update step run lines
34-
updated_lines = update_lines(step["run"], "set-env", "$GITHUB_ENV")
32+
# Update step run lines
33+
updated_lines = update_lines(step["run"], "set-env", "$GITHUB_ENV")
3534

36-
# Keep track of change counts
37-
if updated_lines != step["run"]:
38-
self.count += 1
39-
step["run"] = updated_lines
35+
# Keep track of change counts
36+
if updated_lines != step["run"]:
37+
self.count += 1
38+
step["run"] = updated_lines
4039

4140
return self.count != 0

action_updater/main/updaters/setoutput/update.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,24 +49,23 @@ def detect(self, action):
4949
self.count = 0
5050

5151
# No point if we don't have jobs!
52-
if not action.jobs:
52+
if not action.steps:
5353
return False
5454

5555
# For each job, look for steps->updater versions
56-
for _, job in action.jobs.items():
57-
for step in job.get("steps", []):
56+
for step in action.steps:
5857

59-
# We are primarily interested in uses
60-
if "run" not in step:
61-
continue
58+
# We are primarily interested in uses
59+
if "run" not in step:
60+
continue
6261

63-
# Update step run lines (returns parsed again together)
64-
updated_lines = update_lines(step["run"])
62+
# Update step run lines (returns parsed again together)
63+
updated_lines = update_lines(step["run"])
6564

66-
# Keep track of change counts
67-
if updated_lines != step["run"]:
68-
self.count += 1
65+
# Keep track of change counts
66+
if updated_lines != step["run"]:
67+
self.count += 1
6968

70-
step["run"] = updated_lines
69+
step["run"] = updated_lines
7170

7271
return self.count != 0

action_updater/main/updaters/version/update.py

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -30,65 +30,64 @@ def detect(self, action):
3030
self.count = 0
3131

3232
# No point if we don't have jobs!
33-
if not action.jobs:
33+
if not action.steps:
3434
return False
3535

3636
# We will use major versions for these orgs (trusted)
3737
trusted_orgs = self.settings.get("major_orgs")
3838

3939
# For each job, look for steps->updater versions
40-
for _, job in action.jobs.items():
41-
for step in job.get("steps", []):
40+
for step in action.steps:
4241

43-
# We are primarily interested in uses
44-
if "uses" not in step:
45-
continue
42+
# We are primarily interested in uses
43+
if "uses" not in step:
44+
continue
4645

47-
repo = step["uses"]
46+
repo = step["uses"]
4847

49-
# If we have a local action, nothing to update
50-
if repo.startswith("./"):
51-
continue
48+
# If we have a local action, nothing to update
49+
if repo.startswith("./"):
50+
continue
5251

53-
# Get the current tag or version (we will want to maintain this convention)
54-
# Create a lookup based on the ref
55-
repo, _ = repo.split("@")
56-
org, _ = repo.split("/", 1)
52+
# Get the current tag or version (we will want to maintain this convention)
53+
# Create a lookup based on the ref
54+
repo, _ = repo.split("@")
55+
org, _ = repo.split("/", 1)
5756

58-
# Retrieve all tags for the repository, a lookup by tag name
59-
tags = self.cache["tags"].get(repo) or self.get_tags_lookup(repo)
57+
# Retrieve all tags for the repository, a lookup by tag name
58+
tags = self.cache["tags"].get(repo) or self.get_tags_lookup(repo)
6059

61-
updated = None
62-
if trusted_orgs and org in trusted_orgs:
63-
updated = self.get_major_tag(tags)
60+
updated = None
61+
if trusted_orgs and org in trusted_orgs:
62+
updated = self.get_major_tag(tags)
6463

65-
if not updated:
66-
updated = self.get_tagged_commit(tags)
64+
if not updated:
65+
updated = self.get_tagged_commit(tags)
6766

68-
# If we don't have tags by this point, no go - we cannot parse
69-
if not updated:
70-
continue
71-
updated = f"{repo}@{updated}"
72-
previous = step["uses"]
67+
# If we don't have tags by this point, no go - we cannot parse
68+
if not updated:
69+
continue
70+
updated = f"{repo}@{updated}"
71+
previous = step["uses"]
7372

74-
# If we added a new comment, update the old one
75-
if "#" in updated:
76-
updated, comment = updated.split("#", 1)
77-
comment = comment.strip()
78-
step["uses"] = updated.strip()
73+
# If we added a new comment, update the old one
74+
if "#" in updated:
75+
updated, comment = updated.split("#", 1)
76+
comment = comment.strip()
77+
step["uses"] = updated.strip()
7978

80-
# TODO some check to preserve other previous comments?
81-
step.ca.items["uses"] = [None, None, None, None]
79+
# TODO some check to preserve other previous comments?
80+
step.ca.items["uses"] = [None, None, None, None]
8281

83-
# Add the end of line comment (third position in list)
84-
step.yaml_add_eol_comment(f"# {comment}\n", "uses", column=0)
82+
# Add the end of line comment (third position in list)
83+
step.yaml_add_eol_comment(f"# {comment}\n", "uses", column=0)
8584

86-
# Always do the update (regardless of comment!)
87-
step["uses"] = updated.strip()
85+
# Always do the update (regardless of comment!)
86+
step["uses"] = updated.strip()
8887

89-
# Do we have a change?
90-
if step["uses"] != previous:
91-
self.count += 1
88+
# Do we have a change?
89+
if step["uses"] != previous:
90+
self.count += 1
9291

9392
return self.count != 0
9493

0 commit comments

Comments
 (0)