Repository: nektos/act (language: go)

Issue:
Artifact Server - Path separator issue on downloads
### Bug report info

```plain text
act version:            0.2.46
GOOS:                   windows
GOARCH:                 amd64
NumCPU:                 16
Docker host:            DOCKER_HOST environment variable is not set
Sockets found:
        \\.\pipe\docker_engine(broken)
Config files:
        C:\Users\Reisen\.actrc:
                -P ubuntu-latest=catthehacker/ubuntu:act-latest
                -P ubuntu-22.04=catthehacker/ubuntu:act-22.04
                -P ubuntu-20.04=catthehacker/ubuntu:act-20.04
                -P ubuntu-18.04=catthehacker/ubuntu:act-18.04
                --artifact-server-path T:\act
Build info:
        Go version:            go1.18.10
        Module path:           github.com/nektos/act
        Main version:          (devel)
        Main path:             github.com/nektos/act
        Main checksum:
        Build settings:
                -compiler:            gc
                -ldflags:             -s -w -X main.version=0.2.46 -X main.commit=b0d0cec71f2240fc629dcaa1889ed8c4ad52beb9 -X main.date=2023-06-01T02:39:24Z -X main.builtBy=goreleaser
                CGO_ENABLED:          0
                GOARCH:               amd64
                GOOS:                 windows
                GOAMD64:              v1
                vcs:                  git
                vcs.revision:         b0d0cec71f2240fc629dcaa1889ed8c4ad52beb9
                vcs.time:             2023-06-01T02:39:05Z
                vcs.modified:         false
Docker Engine:
        Engine version:        24.0.2
        Engine runtime:        runc
        Cgroup version:        1
        Cgroup driver:         cgroupfs
        Storage driver:        overlay2
        Registry URI:          https://index.docker.io/v1/
        OS:                    Docker Desktop
        OS type:               linux
        OS version:
        OS arch:               x86_64
        OS kernel:             5.10.102.1-microsoft-standard-WSL2
        OS CPU:                16
        OS memory:             51345 MB
        Security options:
                name=seccomp,profile=builtin
```


### Command used with act

```sh
act
```


### Describe issue

Artifact server json outputs contain backslashes as path separators when running on windows (response from the artifact test yaml):
```json
{"value":[{"path":"Artifact-A\\file1.txt","itemType":"file","contentLocation":"http://10.129.191.30:34567/artifact/1//Artifact-A\\file1.txt"},{"path":"Big-Artifact\\file6.rnd","itemType":"file","contentLocation":"http://10.129.191.30:34567/artifact/1//Big-Artifact\\file6.rnd"},{"path":"Big-Uploaded-Twice\\file7.rnd","itemType":"file","contentLocation":"http://10.129.191.30:34567/artifact/1//Big-Uploaded-Twice\\file7.rnd"},{"path":"GZip-Artifact\\gzip.txt","itemType":"file","contentLocation":"http://10.129.191.30:34567/artifact/1//GZip-Artifact\\gzip.txt"},{"path":"Mid-Size-Artifact\\file5.rnd","itemType":"file","contentLocation":"http://10.129.191.30:34567/artifact/1//Mid-Size-Artifact\\file5.rnd"},{"path":"Multi-Path-Artifact\\dir-1\\file1.txt","itemType":"file","contentLocation":"http://10.129.191.30:34567/artifact/1//Multi-Path-Artifact\\dir-1\\file1.txt"},{"path":"Multi-Path-Artifact\\dir-2\\file2.txt","itemType":"file","contentLocation":"http://10.129.191.30:34567/artifact/1//Multi-Path-Artifact\\dir-2\\file2.txt"},{"path":"artifact\\to\\dir-1\\file1.txt","itemType":"file","contentLocation":"http://10.129.191.30:34567/artifact/1//artifact\\to\\dir-1\\file1.txt"},{"path":"artifact\\to\\dir-2\\file2.txt","itemType":"file","contentLocation":"http://10.129.191.30:34567/artifact/1//artifact\\to\\dir-2\\file2.txt"},{"path":"artifact\\to\\dir-3\\gzip.txt","itemType":"file","contentLocation":"http://10.129.191.30:34567/artifact/1//artifact\\to\\dir-3\\gzip.txt"},{"path":"artifact\\to\\dir-5\\file5.rnd","itemType":"file","contentLocation":"http://10.129.191.30:34567/artifact/1//artifact\\to\\dir-5\\file5.rnd"},{"path":"artifact\\to\\dir-6\\file6.rnd","itemType":"file","contentLocation":"http://10.129.191.30:34567/artifact/1//artifact\\to\\dir-6\\file6.rnd"},{"path":"artifact\\to\\dir-7\\file7.rnd","itemType":"file","contentLocation":"http://10.129.191.30:34567/artifact/1//artifact\\to\\dir-7\\file7.rnd"},{"path":"my-artifact\\world.txt","itemType":"file","contentLocation":"http://10.129.191.30:34567/artifact/1//my-artifact\\world.txt"}]}
```

This, in turn, makes `actions/download-artifact` download files with the backslash as part of the artifact name
e.g an artifact previously uploaded as `artifact.tar`, will be downloaded as `\artifact.tar`, backslash included

Just for the sake of identifying the issue, replacing "\\" with "/"  on the json response paths makes the downloaded file work as expected, but I'm not sure its the only place where the outputs have bad paths in them.

https://github.com/nektos/act/blob/310cb79e81e07ef11985b2f934092b82b167ccf1/pkg/artifacts/server.go#L225C5-L231C7
```go				
rel = strings.TrimSuffix(rel, gzipExtension)
path := filepath.Join(itemPath, rel)

rel = strings.Replace(rel, "\\", "/", -1)
path = strings.Replace(path, "\\", "/", -1)

files = append(files, ContainerItem{
	Path:            path,
	ItemType:        "file",
	ContentLocation: fmt.Sprintf("http://%s/artifact/%s/%s/%s", req.Host, container, itemPath, rel),
})
```



### Link to GitHub repository

_No response_

### Workflow content

```yml
name: Test

on: push

jobs:
  upload:
    runs-on: ubuntu-latest
    steps:
    - run: | 
        echo hello > world.txt
    - uses: actions/upload-artifact@v3
      with:
        name: files
        path: world.txt

  download:
    runs-on: ubuntu-latest
    needs: [ upload ]
    steps:
    - uses: actions/download-artifact@v3
      with:
        name: files
    - run: ls -al
```


### Relevant log output

```sh
time="2023-07-07T08:40:44-03:00" level=info msg="Start server on http://10.129.191.30:34567"
[Test/upload] 🚀  Start image=catthehacker/ubuntu:act-latest
[Test/upload]   🐳  docker pull image=catthehacker/ubuntu:act-latest platform= username= forcePull=true
[Test/upload]   🐳  docker create image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[]
[Test/upload]   🐳  docker run image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[]
[Test/upload]   ☁  git clone 'https://github.com/actions/upload-artifact' # ref=v3
[Test/upload] ⭐ Run Main echo hello > world.txt
[Test/upload]   🐳  docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/0] user= workdir=
[Test/upload]   ✅  Success - Main echo hello > world.txt
[Test/upload] ⭐ Run Main actions/upload-artifact@v3
[Test/upload]   🐳  docker cp src=C:\Users\Reisen\.cache\act/actions-upload-artifact@v3/ dst=/var/run/act/actions/actions-upload-artifact@v3/
[Test/upload]   🐳  docker exec cmd=[node /var/run/act/actions/actions-upload-artifact@v3/dist/index.js] user= workdir=
[Test/upload]   💬  ::debug::followSymbolicLinks 'true'
[Test/upload]   💬  ::debug::implicitDescendants 'true'
[Test/upload]   💬  ::debug::omitBrokenSymbolicLinks 'true'
[Test/upload]   💬  ::debug::followSymbolicLinks 'true'
[Test/upload]   💬  ::debug::implicitDescendants 'true'
[Test/upload]   💬  ::debug::matchDirectories 'true'
[Test/upload]   💬  ::debug::omitBrokenSymbolicLinks 'true'
[Test/upload]   💬  ::debug::Search path '/mnt/x/Wordir/Test/world.txt'
[Test/upload]   💬  ::debug::File:/mnt/x/Wordir/Test/world.txt was found using the provided searchPath
| With the provided path, there will be 1 file uploaded
[Test/upload]   💬  ::debug::Root artifact directory is /mnt/x/Wordir/Test
| Starting artifact upload
| For more detailed logs during the artifact upload process, enable step-debugging: https://docs.github.com/actions/monitoring-and-troubleshooting-workflows/enabling-debug-logging#enabling-step-debug-logging
| Artifact name is valid!
[Test/upload]   💬  ::debug::Artifact Url: http://10.129.191.30:34567/_apis/pipelines/workflows/1/artifacts?api-version=6.0-preview
[Test/upload]   💬  ::debug::Upload Resource URL: http://10.129.191.30:34567/upload/1
| Container for artifact "files" successfully created. Starting upload of file(s)
[Test/upload]   💬  ::debug::File Concurrency: 2, and Chunk Size: 8388608
[Test/upload]   💬  ::debug::/mnt/x/Wordir/Test/world.txt is less than 64k in size. Creating a gzip file in-memory to potentially reduce the upload size
[Test/upload]   💬  ::debug::The gzip file created for /mnt/x/Wordir/Test/world.txt did not help with reducing the size of the file. The original file will be uploaded as-is
| Total size of all the files uploaded is 6 bytes
| File upload process has finished. Finalizing the artifact upload
[Test/upload]   💬  ::debug::Artifact Url: http://10.129.191.30:34567/_apis/pipelines/workflows/1/artifacts?api-version=6.0-preview
[Test/upload]   💬  ::debug::URL is http://10.129.191.30:34567/_apis/pipelines/workflows/1/artifacts?api-version=6.0-preview&artifactName=files
[Test/upload]   💬  ::debug::Artifact files has been successfully uploaded, total size in bytes: 6
| Artifact has been finalized. All files have been successfully uploaded!
|
| The raw size of all the files that were specified for upload is 6 bytes
| The size of all the files that were uploaded is 6 bytes. This takes into account any gzip compression used to reduce the upload size, time and storage
|
| Note: The size of downloaded zips can differ significantly from the reported size. For more information see: https://github.com/actions/upload-artifact#zipped-artifact-downloads
|
| Artifact files has been successfully uploaded!
[Test/upload]   ✅  Success - Main actions/upload-artifact@v3
[Test/upload] 🏁  Job succeeded
[Test/download] 🚀  Start image=catthehacker/ubuntu:act-latest
[Test/download]   🐳  docker pull image=catthehacker/ubuntu:act-latest platform= username= forcePull=true
[Test/download]   🐳  docker create image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[]
[Test/download]   🐳  docker run image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[]
[Test/download]   ☁  git clone 'https://github.com/actions/download-artifact' # ref=v3
[Test/download] ⭐ Run Main actions/download-artifact@v3
[Test/download]   🐳  docker cp src=C:\Users\Reisen\.cache\act/actions-download-artifact@v3/ dst=/var/run/act/actions/actions-download-artifact@v3/
[Test/download]   🐳  docker exec cmd=[node /var/run/act/actions/actions-download-artifact@v3/dist/index.js] user= workdir=
[Test/download]   💬  ::debug::Resolved path is /mnt/x/Wordir/Test
| Starting download for files
[Test/download]   💬  ::debug::Artifact Url: http://10.129.191.30:34567/_apis/pipelines/workflows/1/artifacts?api-version=6.0-preview
| Directory structure has been setup for the artifact
[Test/download]   💬  ::debug::Download file concurrency is set to 2
| Total number of files that will be downloaded: 1
[Test/download]   💬  ::debug::/mnt/x/Wordir/Test/\world.txt size:(6) blksize:(4096) blocks:(8)
| Artifact files was downloaded to /mnt/x/Wordir/Test
| Artifact download has finished successfully
[Test/download]   ✅  Success - Main actions/download-artifact@v3
[Test/download]   ⚙  ::set-output:: download-path=/mnt/x/Wordir/Test
[Test/download] ⭐ Run Main ls -al
[Test/download]   🐳  docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/1] user= workdir=
| total 12
| drwxr-xr-x 2 root root 4096 Jul  7 11:40  .
| drwxr-xr-x 3 root root 4096 Jul  7 11:40  ..
| -rw-r--r-- 1 root root    6 Jul  7 11:40 '\world.txt'
[Test/download]   ✅  Success - Main ls -al
[Test/download] 🏁  Job succeeded
```


### Additional information

_No response_


Discussion / hints:

Fix patch:
diff --git a/pkg/artifacts/server.go b/pkg/artifacts/server.go
index d0c7a6aa76a..daaacf666db 100644
--- a/pkg/artifacts/server.go
+++ b/pkg/artifacts/server.go
@@ -223,9 +223,13 @@ func downloads(router *httprouter.Router, baseDir string, fsys fs.FS) {
 
 				// if it was upload as gzip
 				rel = strings.TrimSuffix(rel, gzipExtension)
+				path := filepath.Join(itemPath, rel)
+
+				rel = filepath.ToSlash(rel)
+				path = filepath.ToSlash(path)
 
 				files = append(files, ContainerItem{
-					Path:            filepath.Join(itemPath, rel),
+					Path:            path,
 					ItemType:        "file",
 					ContentLocation: fmt.Sprintf("http://%s/artifact/%s/%s/%s", req.Host, container, itemPath, rel),
 				})


Test patch: