Skip to content

Commit 64029d1

Browse files
authored
Pin actions by SHA (#15)
* Pin actions by SHA * . * . * . * .
1 parent 62b94c4 commit 64029d1

11 files changed

+120
-195
lines changed

.github/CODEOWNERS

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
* @pavelzw @0xbe7a @AdrianFreundQC
1+
* @pavelzw

.github/workflows/bump-versions.yml

+4-4
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@ jobs:
1515
name: gh-actions
1616
steps:
1717
- name: Checkout branch
18-
uses: actions/checkout@v4
18+
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
1919
- name: Set up pixi
20-
uses: prefix-dev/setup-pixi@ba3bb36eb2066252b2363392b7739741bb777659
20+
uses: prefix-dev/setup-pixi@ba3bb36eb2066252b2363392b7739741bb777659 # v0.8.1
2121
with:
2222
activate-environment: true
2323
- name: Update ${{ matrix.name }}
2424
run: python -m scripts.${{ matrix.script }}
2525
env:
2626
GH_TOKEN: ${{ github.token }}
27-
- uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c
27+
- uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
2828
with:
2929
commit-message: Auto-update ${{ matrix.name }}
3030
title: Auto-update ${{ matrix.name }}
@@ -35,7 +35,7 @@ jobs:
3535
delete-branch: true
3636
- name: Create issue on failure
3737
if: failure()
38-
uses: actions/github-script@v7
38+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
3939
with:
4040
script: |
4141
github.rest.issues.listForRepo({

.github/workflows/ci-copier.yml

+14-8
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ jobs:
1414
runs-on: ubuntu-latest
1515
steps:
1616
- name: Checkout branch
17-
uses: actions/checkout@v4
17+
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
1818
- name: Set up pixi
19-
uses: prefix-dev/setup-pixi@ba3bb36eb2066252b2363392b7739741bb777659
19+
uses: prefix-dev/setup-pixi@ba3bb36eb2066252b2363392b7739741bb777659 # v0.8.1
2020
with:
2121
environments: default lint
2222
- name: pre-commit
@@ -27,9 +27,9 @@ jobs:
2727
runs-on: ubuntu-latest
2828
steps:
2929
- name: Checkout branch
30-
uses: actions/checkout@v4
30+
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
3131
- name: Set up pixi
32-
uses: prefix-dev/setup-pixi@ba3bb36eb2066252b2363392b7739741bb777659
32+
uses: prefix-dev/setup-pixi@ba3bb36eb2066252b2363392b7739741bb777659 # v0.8.1
3333
- name: Test
3434
run: pixi run test --color=yes
3535
env:
@@ -45,13 +45,17 @@ jobs:
4545
minimal-python-version: [py38, py310]
4646
steps:
4747
- name: Checkout branch
48-
uses: actions/checkout@v4
48+
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
4949
with:
5050
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
5151
- name: Set up pixi
52-
uses: prefix-dev/setup-pixi@ba3bb36eb2066252b2363392b7739741bb777659
52+
uses: prefix-dev/setup-pixi@ba3bb36eb2066252b2363392b7739741bb777659 # v0.8.1
5353
with:
5454
activate-environment: true
55+
- name: Generate branch name
56+
id: branch
57+
run: |
58+
echo "name=ci/$GITHUB_SHA-${{ matrix.minimal-python-version }}" >> $GITHUB_OUTPUT
5559
- name: Test generated package CI
5660
run: |
5761
# Name of the generated package.
@@ -86,10 +90,10 @@ jobs:
8690
git commit -m "Create pixi.lock"
8791
# Push the generated package's HEAD commit to a `ci/*` branch
8892
cid=$(git rev-parse HEAD)
89-
git push -f "${GITHUB_SERVER_URL/https:\/\//git@}:$GITHUB_REPOSITORY" $cid:refs/heads/ci/${GITHUB_SHA}-${{ matrix.minimal-python-version }}
93+
git push -f "${GITHUB_SERVER_URL/https:\/\//git@}:$GITHUB_REPOSITORY" $cid:refs/heads/${{ steps.branch.outputs.name }}
9094
# Use the GitHub API to wait for the generated package's CI to complete (success or failure).
9195
# We look for a GitHub Actions run for the HEAD commit ID.
92-
WORKFLOW_URL="$GITHUB_API_URL/repos/${GITHUB_REPOSITORY}/actions/runs?branch=ci/${GITHUB_SHA}-${{ matrix.minimal-python-version }}&head_sha=${cid}"
96+
WORKFLOW_URL="$GITHUB_API_URL/repos/${GITHUB_REPOSITORY}/actions/runs?branch=${{ steps.branch.outputs.name }}&head_sha=${cid}"
9397
echo "Waiting for inner CI to start"
9498
while (( $(curl -Ls --header "$AUTH" "$WORKFLOW_URL" | jq -r ".workflow_runs | length") < 1 )); do
9599
sleep 10
@@ -106,12 +110,14 @@ jobs:
106110
- name: Clean up CI branch
107111
if: always()
108112
run: |
113+
set -x
109114
AUTH='authorization: Bearer ${{ secrets.GITHUB_TOKEN }}'
110115
eval $(ssh-agent)
111116
ssh-add - <<< "${{ secrets.SSH_PRIVATE_KEY }}"
112117
113118
git push -d "${GITHUB_SERVER_URL/https:\/\//git@}:$GITHUB_REPOSITORY" refs/heads/ci/$GITHUB_SHA-${{ matrix.minimal-python-version }}
114119
120+
cid=$(git rev-parse HEAD)
115121
for line in $(curl -Ls --header "$AUTH" "$GITHUB_API_URL/repos/${GITHUB_REPOSITORY}/actions/runs?branch=ci/${GITHUB_SHA}-${{ matrix.minimal-python-version }}&head_sha=${cid}" | jq -r ".workflow_runs | .[] | select(.status != \"completed\") | .id")
116122
do
117123
curl -Ls --header "$AUTH" --request POST "$GITHUB_API_URL/repos/${GITHUB_REPOSITORY}/actions/runs/$line/cancel" > /dev/null

pixi.lock

+4-97
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pixi.toml

+9-5
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,20 @@ platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"]
88
python = ">=3.12"
99
copier = ">=9.3.1,<10"
1010
pytest = ">=8.2,<9"
11-
"ruamel.yaml" = ">=0.17,<0.18"
12-
"ruamel.yaml.jinja2" = ">=0.2.4,<0.3"
11+
"ruamel.yaml" = "*"
1312
mypy = "*"
1413
pre-commit = "*"
1514
gh = "*"
1615
go-yq = "*"
1716

17+
[tasks]
18+
test = "pytest"
19+
generate-temp-repo = """
20+
export COPIER_PATH="$(mktemp -d)" && \
21+
copier copy --trust --vcs-ref=HEAD . "$COPIER_PATH" && \
22+
echo "Generated repo to $COPIER_PATH"
23+
"""
24+
1825
[feature.lint.dependencies]
1926
pre-commit = "*"
2027
insert-license-header = "*"
@@ -28,9 +35,6 @@ typos = "*"
2835
pre-commit-install = "pre-commit install"
2936
pre-commit-run = "pre-commit run -a"
3037

31-
[tasks]
32-
test = "pytest"
33-
3438
[environments]
3539
default = []
3640
lint = { features = ["lint"], no-default-feature = true }

scripts/update_actions.py

+32-46
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,42 @@
11
import os
22
from pathlib import Path
33

4-
import ruamel.yaml
5-
64
from scripts.common import get_latest_github_tag
75

86

9-
def get_latest_pin(action: str, current_version: str) -> str:
10-
latest_tag_name, latest_tag_sha = get_latest_github_tag(action)
11-
12-
pin_by_major = current_version.startswith("v")
13-
14-
if pin_by_major:
15-
major = latest_tag_name.split(".")[0]
16-
return major
17-
else:
18-
return latest_tag_sha
19-
20-
21-
def update_workflow_actions(file_path: Path, dry_run: bool = False):
22-
yaml = ruamel.yaml.YAML(typ="jinja2")
23-
yaml.preserve_quotes = True
24-
yaml.indent(mapping=2, sequence=4, offset=2)
25-
yaml.width = 1000 # Prevent default wrapping
26-
27-
with open(file_path) as f:
28-
workflow = yaml.load(f)
29-
30-
for job in workflow.get("jobs", {}).values():
31-
for step in job.get("steps", []):
32-
if "uses" in step:
33-
action_ref = step["uses"]
34-
action, _, current_version = action_ref.partition("@")
35-
new_version = get_latest_pin(action, current_version)
36-
print(
37-
f"{action}:"
38-
f" current version: {current_version},"
39-
f" latest version: {new_version}"
40-
)
41-
if new_version != current_version and not dry_run:
42-
step["uses"] = f"{action}@{new_version}"
43-
44-
with open(file_path, "w") as f:
45-
yaml.dump(workflow, f)
46-
47-
48-
def update_project_workflows(
49-
path: str = "template/.github/workflows/", dry_run: bool = False
50-
):
7+
def update_workflow_actions(file_path: Path):
8+
text = file_path.read_text()
9+
lines = text.splitlines(keepends=True)
10+
11+
new_lines: list[str] = []
12+
13+
for line in lines:
14+
if "uses: " not in line:
15+
new_lines.append(line)
16+
else:
17+
action_with_rest = line.split(":")[1].strip()
18+
action, current_sha_with_version = action_with_rest.split("@")
19+
current_sha, current_version = current_sha_with_version.split("#")
20+
current_sha = current_sha.strip()
21+
current_version = current_version.strip()
22+
new_version, new_sha = get_latest_github_tag(action)
23+
print(
24+
f"{action}:"
25+
f" current version: {current_version},"
26+
f" latest version: {new_version}"
27+
)
28+
new_line = line.replace(current_sha, new_sha).replace(
29+
current_version, new_version
30+
)
31+
new_lines.append(new_line)
32+
33+
file_path.write_text("".join(new_lines))
34+
35+
36+
def update_project_workflows(path: str = "template/.github/workflows/"):
5137
for file in os.listdir(path):
52-
print(f"Updating {file}")
53-
update_workflow_actions(Path(path) / file, dry_run=dry_run)
38+
print(f"Updating `{file}`")
39+
update_workflow_actions(Path(path) / file)
5440

5541

5642
if __name__ == "__main__":

0 commit comments

Comments
 (0)