Skip to content

Commit 6cdeab9

Browse files
authored
Merge pull request #5 from stackhpc/sanitise-artifact-filenames
Sanitise artifact filenames
2 parents c97c844 + 1bf1140 commit 6cdeab9

File tree

5 files changed

+123
-8
lines changed

5 files changed

+123
-8
lines changed

.github/workflows/multinode.yml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -356,13 +356,14 @@ jobs:
356356
working-directory: ${{ github.workspace }}/terraform-kayobe-multinode
357357
if: ${{ always() && steps.config_ach.outcome == 'success' }}
358358

359-
# NOTE: The tmux log rename is due to GitHub Actions not accepting files with a colon as artifacts.
360-
- name: Fix up deployment log filename
361-
run: |
362-
if [[ -f ${{ github.workspace }}/logs/tmux.kayobe:0.log ]]; then
363-
mv ${{ github.workspace }}/logs/tmux.kayobe:0.log ${{ github.workspace }}/logs/tmux.kayobe.log
364-
fi
365-
working-directory: ${{ github.workspace }}/terraform-kayobe-multinode
359+
# GitHub Actions does not accept filenames with certain characters, and
360+
# fails the upload-artifact action if any exist. The tmux log file
361+
# contains a colon, as do previous Tempest results directories.
362+
- name: Sanitise artifact filenames
363+
uses: stackhpc/stackhpc-openstack-gh-workflows/[email protected]
364+
with:
365+
path: |
366+
${{ github.workspace }}/logs/
366367
if: ${{ always() && steps.config_ach.outcome == 'success' }}
367368

368369
- name: Upload test result artifacts

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.
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)