A deploy job that removed one name: line began searching for release-input/site.tar.gz, while actions/download-artifact@v4 had correctly placed the file at release-input/web-package/site.tar.gz. The workflow still passed its build job, still downloaded an artifact, and failed only when the release script tried to extract a path that no longer existed.
That is the migration risk worth testing: not whether GitHub Actions can download the artifact, but whether the downloaded filesystem still satisfies the contract your deploy command assumes. Artifact v4 improves availability and uses a new artifact backend, but it also draws a hard compatibility line with v1–v3 artifacts and makes selector choice visible in the directory layout.
1. Start with a filesystem contract, not a version bump
Before changing either action version, write down the exact file that the deploy job consumes. In a build-and-deploy pipeline, the meaningful contract is usually one archive, such as site.tar.gz, plus a small identity file such as build-sha.txt. The upload name is an artifact identifier; it is not always a directory that will appear below your requested download path.
For this guide, the build job uploads one artifact named web-package. Its contents are a compressed static site archive and a commit marker. The deploy job must download the archive before running its extraction command.
| Contract element | Expected value | Why it matters |
|---|---|---|
| Artifact name | web-package |
The selector used by the deploy job. |
| Archive file | site.tar.gz |
The file passed to tar. |
| Identity marker | build-sha.txt |
Confirms which build output was selected. |
| Named-download destination | release-input/site.tar.gz |
The correct v4 path when name: web-package is present. |
This contract prevents a common incorrect workaround: adding an artifact-name directory to every deploy path. That directory is expected when downloading all artifacts, but it is not the normal result of downloading one artifact by name into a specified path.
2. Test the selector first: v4 layout depends on how you download
The same uploaded artifact can produce three different layouts. Treat name, no selector, and pattern plus merge-multiple as separate behaviors rather than interchangeable refactors. A deploy job consuming one archive should normally use the first row.
| Download selector | Requested path | Expected v4 result | Migration meaning |
|---|---|---|---|
name: web-package |
release-input |
release-input/site.tar.gz |
Single-artifact deploy; keep existing archive-oriented paths. |
No name; download all artifacts |
release-input |
release-input/web-package/site.tar.gz |
Artifact names become directory names. |
pattern: web-package-* with merge-multiple: true |
release-input |
Matched files merge directly under release-input |
Only safe when duplicate filenames are deliberately handled. |
Run this diagnostic in a temporary migration branch immediately after the download step. It prints the actual tree rather than relying on a mental model carried over from an older workflow.
- name: Inspect downloaded artifact layout
shell: bash
run: |
echo "Downloaded files:"
find release-input -maxdepth 3 -type f -print | sort
test -f release-input/site.tar.gz
test -f release-input/build-sha.txt
If this assertion fails, do not change the assertion until you decide whether the selector changed intentionally. A missing name is a semantic change: it expands the input set from one known artifact to every artifact in the selected run.
3. Upgrade upload and download together across the compatibility boundary
Artifacts v4 is not cross-compatible with previous artifact versions. An artifact uploaded with actions/upload-artifact@v3 cannot be consumed by actions/download-artifact@v4. The reliable migration unit is therefore the producer and consumer pair, not the individual YAML line that happens to fail first.
| Producer | Consumer | Migration result | Recommended action |
|---|---|---|---|
upload-artifact@v3 |
download-artifact@v3 |
Legacy-compatible pair | Use only as the pre-migration baseline. |
upload-artifact@v4 |
download-artifact@v4 |
Compatible v4 pair | Use for the migrated build-and-deploy path. |
upload-artifact@v3 |
download-artifact@v4 |
Not cross-compatible | Do not mix versions within one artifact handoff. |
upload-artifact@v4 |
download-artifact@v3 |
Do not treat as a supported migration path | Upgrade the consumer with the producer. |
This matters when repositories have reusable workflows. A central build workflow may upload artifacts while application repositories own deployment workflows. Search both repositories before merging: the visible deployment file may not contain the upload action that determines compatibility.
git grep -nE 'actions/(upload-artifact|download-artifact)@v[1-4]' \
-- .github/workflows
Also inspect workflow files called through uses: owner/repo/.github/workflows/file.yml@ref. Version compatibility is a pipeline property, not merely a property of one job.
4. Make the build artifact inspectable before migrating deployment
Use an archive plus a marker file rather than uploading an unstructured build directory. A tar archive gives the deploy job one stable payload filename, and it is also useful when executable permissions matter because the archive records file modes. The marker makes the selected build observable without requiring a production deploy.
jobs:
build:
runs-on: ubuntu-latest
outputs:
build_sha: ${{ steps.identity.outputs.sha }}
steps:
- uses: actions/checkout@v4
- name: Build site
run: |
npm ci
npm run build
tar -czf site.tar.gz -C dist .
- name: Write build identity
id: identity
shell: bash
run: |
printf '%s\n' "$GITHUB_SHA" > build-sha.txt
echo "sha=$GITHUB_SHA" >> "$GITHUB_OUTPUT"
- name: Upload deploy package
uses: actions/upload-artifact@v4
with:
name: web-package
path: |
site.tar.gz
build-sha.txt
if-no-files-found: error
In a same-run downstream job, $GITHUB_SHA already identifies the same workflow revision. Therefore, this marker primarily proves that the downloaded archive set contains the expected files; it does not independently prove that a different commit was selected. It becomes essential when the deploy workflow selects another run or repository, covered in section 8.
5. Compare the old and new download layouts before changing deploy commands
A migration review should include the selector-to-path matrix next to the workflow diff. This catches the mistake where a reviewer sees a valid v4 action and misses that the action is no longer downloading the same set of inputs.
| Configuration | Files below release-input |
Deploy command should reference |
|---|---|---|
name: web-package |
site.tar.gz, build-sha.txt |
release-input/site.tar.gz |
No name, download all |
web-package/site.tar.gz, plus one directory per artifact |
release-input/web-package/site.tar.gz |
pattern and merge-multiple: true |
Matched files merged into the requested directory | release-input/site.tar.gz, only if collisions are impossible or accepted |
The migration-safe download step for this single-package deployment is explicit:
- name: Download deploy package
uses: actions/download-artifact@v4
with:
name: web-package
path: release-input
Do not rely on an unqualified “download all” because it happens to find the package today. A later test-report artifact, SBOM, or debug-log upload changes the deploy job’s input set without touching the deploy YAML.
6. Keep name: web-package when one archive is the deploy input
The deploy decision is straightforward: if the job consumes one archive, retain name: web-package. Removing name is not a cleanup refactor; it changes the meaning from “download this release package” to “download every artifact from this run.” That change requires either updating the archive path to include web-package or deliberately treating artifact-name directories as part of the deployment contract.
Here is a complete downstream job for the named-artifact case. Notice that the assertions occur before extraction and before any remote deployment command.
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download deploy package
uses: actions/download-artifact@v4
with:
name: web-package
path: release-input
- name: Verify release input
shell: bash
run: |
set -euo pipefail
test -f release-input/site.tar.gz
test -f release-input/build-sha.txt
test "$(cat release-input/build-sha.txt)" = "$GITHUB_SHA"
tar -tzf release-input/site.tar.gz | head -n 20
- name: Extract release
shell: bash
run: |
mkdir -p release-output
tar -xzf release-input/site.tar.gz -C release-output
The needs: build dependency still matters even though v4 artifacts become available after upload. Immediate availability helps API and artifact access behavior, but it does not replace the ordering requirement for a deploy job that must wait for a successful build.
7. Test v4 with a failure-oriented pull request workflow
Do not make production deployment the first test of artifact v4. Add a pull-request-safe verification job that builds, uploads, downloads, lists the result, checks the marker, and validates the archive. This exercises the actual artifact backend and the actual runner filesystem rather than only validating YAML syntax.
- Upgrade the build job to
actions/upload-artifact@v4. - Upgrade the consuming test or deploy-preflight job to
actions/download-artifact@v4. - Keep the intended selector explicit: use
namefor one release package. - Print a bounded directory tree with
find. - Fail on missing files with
test -fbefore invoking cloud, SSH, or package-manager deployment steps.
- name: Assert artifact contract
shell: bash
run: |
set -euo pipefail
find release-input -maxdepth 3 -type f -print | sort
test -s release-input/site.tar.gz
test -s release-input/build-sha.txt
tar -tzf release-input/site.tar.gz > /dev/null
The tradeoff nobody mentions is diagnosis time. Without these four checks, a wrong path may surface later as an empty upload, a missing container build context, or a deployment tool’s generic “file not found” message. With them, the failed job names the broken artifact contract directly.
8. Add stronger identity checks for artifacts from another run or repository
Downloading from another workflow run or repository is where a marker becomes more than a convenient diagnostic. The current deploy workflow’s GITHUB_SHA may not match the commit that produced the selected artifact, so comparing the marker to the deploy workflow SHA can reject a valid release or accept the wrong assumption.
Instead, make the producing build run emit its commit SHA as an explicit output or publish it through the promotion mechanism that selects the run. For example, a dispatch input named expected_build_sha can travel with the selected run_id.
on:
workflow_dispatch:
inputs:
build_run_id:
required: true
type: string
expected_build_sha:
required: true
type: string
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Download selected build artifact
uses: actions/download-artifact@v4
with:
name: web-package
path: release-input
run-id: ${{ inputs.build_run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Verify selected build identity
shell: bash
run: |
set -euo pipefail
test "$(cat release-input/build-sha.txt)" = "${{ inputs.expected_build_sha }}"
test -f release-input/site.tar.gz
Use the token and repository inputs required by your cross-repository design, with only the permissions needed to read the chosen artifact. The key migration rule is independent of the authorization model: validate against the selected build’s identity, not automatically against the deploy run’s identity.
9. Ship the migration this week with a narrow rollout checklist
Make one artifact handoff boring before standardizing every workflow in the organization. Start with a build-to-deploy path that has a single named package, then use the resulting assertions and path contract as a reusable workflow template.
- Search workflow repositories for both upload and download action versions.
- Upgrade each producer-consumer pair to v4 together.
- Document whether every consumer downloads one named artifact, all artifacts, or a merged pattern.
- Add the selector-to-layout matrix to the pull request description for deploy-related changes.
- Run
find,test -f, and archive validation in a non-production workflow run. - Keep
name: web-packagefor a job that consumes one archive. - For cross-run promotion, compare
build-sha.txtwith an expected SHA supplied by the selected build.
The successful v4 migration is not the one where every action reference says @v4. It is the one where the artifact producer, selector, download directory, validation command, and deploy script all agree on the same concrete path.