Gotcha: Don't try and authenticate to URLs generated by GitHub Actions Artifacts v4
I recently spotted GitHub's announcement about the new v4 release for working with GitHub Actions Artifacts and impressed with the performance increase they mentioned, wanted to jump on it.
However, I noticed that my API requests to fetch the uploaded artifacts have been failing since I started uploading using the v4 version of the Actions π€
For context, I've been using the excellent go-github library, and have been doing something like:
import "github.com/google/go-github/v58/github"
// ...
var client *github.Client
// ...
u, _, err := client.Actions.DownloadArtifact(ctx, "...", "...", build.ArtifactID, 1)
// ...
resp, err := client.Do(ctx, req, zipFile)
// ...
Notice that I'm re-using the github.Client
to determine the URL to download a given artifact, as well as then performing the download.
It appears that between v3 and v4 of the Actions upload, this has meant that (for some reason) you can no longer send authenticated requests to the static storage location. This may be "I'm holding it wrong", or may be an unintentional side effect, but thought it was worth writing about it as a form of blogumentation.
I've created a sample repo which has a single Actions run which allows us to verify the behaviour change.
We can see from the following Go code:
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"github.com/google/go-github/v58/github"
)
func main() {
ctx := context.Background()
client := github.NewClient(http.DefaultClient).WithAuthToken(os.Getenv("GITHUB_TOKEN"))
v3, _, err := client.Actions.DownloadArtifact(ctx, "jamietanna", "github-v4-issue", 1248209756, 1)
if err != nil {
log.Fatal(err)
}
fmt.Printf("v3: %v\n", v3)
resp, err := http.DefaultClient.Get(v3.String())
if err != nil {
log.Fatal(err)
}
fmt.Printf("http.Client v3: Status %d\n", resp.StatusCode)
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, v3.String(), nil)
ghResp, err := client.Do(ctx, req, nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("github.Client v3: Status %d\n", ghResp.StatusCode)
v4, _, err := client.Actions.DownloadArtifact(ctx, "jamietanna", "github-v4-issue", 1248209411, 1)
if err != nil {
log.Fatal(err)
}
fmt.Printf("v4: %v\n", v4)
resp, err = http.DefaultClient.Get(v4.String())
if err != nil {
log.Fatal(err)
}
fmt.Printf("http.Client v4: Status %d\n", resp.StatusCode)
req, _ = http.NewRequestWithContext(ctx, http.MethodGet, v4.String(), nil)
ghResp, err = client.Do(ctx, req, nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("github.Client v4: Status %d\n", ghResp.StatusCode)
}
That we then get the following output (linebreaks added for readability):
v3: https://pipelinesghubeus8.actions.githubusercontent.com/emX3Njq6l1qrJDF1bWo9bNVi4eQC64vCBMTaoenXjgnzcrY5lm/_apis/pipelines/1/runs/1/signedartifactscontent?artifactName=1mb-v3&urlExpires=2024-02-15T14%3A48%3A56.6160877Z&urlSigningMethod=HMACV2&urlSignature=1e5RyuMzU8AztwX5Upqs%2FIjbKQfqVqnOFv7pMHN8q2Y%3D
http.Client v3: Status 200
github.Client v3: Status 200
v4: https://productionresultssa15.blob.core.windows.net/actions-results/c7e89aef-cd37-4c12-9cd7-bb30fd886b3c/workflow-job-run-0442c3fb-37e5-5ecb-50da-0a737a3d5924/artifacts/7427eae80b26c2a022516adae0ef004a452a257e4cf1c45fc906d6c83c1f0379.zip?rscd=attachment%3B+filename%3D%221mb-v4.zip%22&se=2024-02-15T14%3A57%3A57Z&sig=cxZHg8ZAeTMW2NVN5nu2AdMD24h2%2Fss0UDoriROJEho%3D&sp=r&spr=https&sr=b&st=2024-02-15T14%3A47%3A57Z&sv=2021-12-02
http.Client v4: Status 200
2024/02/15 14:47:58 GET https://productionresultssa15.blob.core.windows.net/actions-results/c7e89aef-cd37-4c12-9cd7-bb30fd886b3c/workflow-job-run-0442c3fb-37e5-5ecb-50da-0a737a3d5924/artifacts/7427eae80b26c2a022516adae0ef004a452a257e4cf1c45fc906d6c83c1f0379.zip?rscd=attachment%3B+filename%3D%221mb-v4.zip%22&se=2024-02-15T14%3A57%3A57Z&sig=cxZHg8ZAeTMW2NVN5nu2AdMD24h2%2Fss0UDoriROJEho%3D&sp=r&spr=https&sr=b&st=2024-02-15T14%3A47%3A57Z&sv=2021-12-02: 400 []
Digging through the code, it appears that this is due to a response from the new static storage location, productionresultssa15.blob.core.windows.net
, which returns:
<?xml version="1.0" encoding="utf-8"?>
<Error>
<Code>InvalidAuthenticationInfo</Code>
<Message>Authentication information is not given in the correct format. Check the value of Authorization header.
RequestId:....
Time:.....</Message>
</Error>
So the TL;DR is purposefully don't authenticate to retrieve the static blobs, as that shouldn't be required, even if they are artifacts from private repos!