Skip to content

Commit e35e4eb

Browse files
committed
Add sanitise-artifact-names action
Sanitise filenames for GitHub Actions artifacts.
1 parent c97c844 commit e35e4eb

File tree

4 files changed

+115
-1
lines changed

4 files changed

+115
-1
lines changed

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Reusable GitHub workflows and actions for StackHPC OpenStack.
77
The following reusable workflows are provided in the `.github/workflows/`
88
directory.
99

10-
## `multinode.yml`
10+
### `multinode.yml`
1111

1212
The `multinode.yml` workflow can be used to create a multinode test cluster and
1313
run tests and/or operations against it.
@@ -17,3 +17,11 @@ Features:
1717
* Inject an SSH key to access the cluster
1818
* Break (pause) the workflow on failure
1919
* Upgrade from one OpenStack release to another
20+
21+
## Actions
22+
23+
The following actions are provided in the top-level directory.
24+
25+
### `sanitise-artifact-filenames`
26+
27+
Sanitise filenames for GitHub Actions artifacts.

sanitise-artifact-filenames/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Sanitise Artifact Filenames
2+
3+
This action sanitises directory and file names for GitHub Actions artifacts.
4+
Example error from the upload-artifact action if you have an invalid path:
5+
6+
> Error: The path for one of the files in artifact is not valid:
7+
> /tempest-artifacts.2024-08-29T18:18+00:00/docker.log. Contains the following
8+
> character: Colon :
9+
>
10+
> Invalid characters include: Double quote ", Colon :, Less than <, Greater than
11+
> >, Vertical bar |, Asterisk *, Question mark ?, Carriage return \r, Line feed
12+
> \n
13+
>
14+
> The following characters are not allowed in files that are uploaded due to
15+
> limitations with certain file systems such as NTFS. To maintain file system
16+
> agnostic behavior, these characters are intentionally not allowed to prevent
17+
> potential problems with downloads on different file systems.
18+
19+
## Usage
20+
21+
```yaml
22+
- name: Sanitise filenames for artifacts
23+
uses: stackhpc/stackhpc-openstack-gh-workflows/sanitise-artifact-filenames@main
24+
with:
25+
path: path/to/artifact/
26+
```
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
name: Sanitise filenames for GitHub Actions artifacts
3+
description: >
4+
Renames files and directories to be accepted by the GitHub Actions
5+
upload-artifact action.
6+
inputs:
7+
path:
8+
description: The directory containing files to be sanitised
9+
required: true
10+
runs:
11+
using: composite
12+
steps:
13+
- name: Sanitise filenames for GitHub Actions artifacts
14+
run: python3 sanitise-artifact-filenames.py ${{ inputs.path }}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/usr/bin/python3
2+
3+
"""
4+
This script sanitises directory and file names for GitHub Actions artifacts.
5+
Example error from the upload-artifact action if you have an invalid path:
6+
7+
Error: The path for one of the files in artifact is not valid:
8+
/tempest-artifacts.2024-08-29T18:18+00:00/docker.log. Contains the following
9+
character: Colon :
10+
11+
Invalid characters include: Double quote ", Colon :, Less than <, Greater than
12+
>, Vertical bar |, Asterisk *, Question mark ?, Carriage return \r, Line feed
13+
\n
14+
15+
The following characters are not allowed in files that are uploaded due to
16+
limitations with certain file systems such as NTFS. To maintain file system
17+
agnostic behavior, these characters are intentionally not allowed to prevent
18+
potential problems with downloads on different file systems.
19+
"""
20+
21+
import os
22+
import sys
23+
import typing as t
24+
25+
26+
def main() -> None:
27+
if len(sys.argv) != 2:
28+
usage()
29+
sys.exit(1)
30+
31+
sanitise(sys.argv[1])
32+
33+
34+
def usage() -> None:
35+
print(f"Usage: {sys.argv[0]} <path>")
36+
37+
38+
def sanitise(path: str) -> None:
39+
# Recursively walk a directory, sanitising subdirectories and files as we go.
40+
# Walk bottom-up to avoid directory renames breaking subsequent paths.
41+
table = translation_table()
42+
for dirpath, dirnames, filenames in os.walk(path, topdown=False, followlinks=False):
43+
for filename in filenames:
44+
sanitise_file_or_dir(filename, table, dirpath)
45+
for dirname in dirnames:
46+
sanitise_file_or_dir(dirname, table, dirpath)
47+
48+
49+
def translation_table() -> t.Dict:
50+
# Return a translation table that translates all disallowed characters to a dash.
51+
disallowed = "\":<>|*?\r\n"
52+
return str.maketrans(disallowed, "-" * len(disallowed))
53+
54+
55+
def sanitise_file_or_dir(path: str, table: t.Dict, dirpath: str) -> None:
56+
# Sanitise a single file or directory.
57+
sanitised = path.translate(table)
58+
if path != sanitised:
59+
print(f"Sanitising {path} as {sanitised} in {dirpath}")
60+
path = os.path.join(dirpath, path)
61+
dirpath = os.path.join(dirpath, sanitised)
62+
os.rename(path, dirpath)
63+
64+
65+
if __name__ == "__main__":
66+
main()

0 commit comments

Comments
 (0)