Skip to content

Commit 34cae61

Browse files
authored
feat: implement Vercel Blob GitHub Action with comprehensive workflow examples (#1)
* Add initial implementation of Vercel Blob Action with README, action.yml, and index.js * Refactor index.js to use ES modules and improve error handling for file uploads * chore: update action.yml, README, and package.json for improved clarity and functionality - Added .gitignore to exclude .cursor files. - Updated action.yml to include a new input for read-write token and improved descriptions for inputs and outputs. - Modified README to clarify usage instructions and added a section for setting up the Vercel Blob token. - Enhanced package.json with a new dependency for @vercel/blob and updated the description for better accuracy. * feat: add GitHub Actions workflow for uploading files to Vercel Blob - Implemented a comprehensive workflow to upload static files, build artifacts, and conditionally upload files based on user input. - Included error handling for uploads and created sample files for testing. - Enhanced the process with clear steps for checking file existence and handling upload results. * chore: update Node.js version in GitHub Actions workflow - Changed the Node.js version from 20 to 22 in the upload-to-blob.yml workflow for improved compatibility and performance. * chore: switch from npm to bun for dependency management in GitHub Actions workflow - Replaced npm commands with bun commands in the upload-to-blob.yml workflow for improved performance and consistency. - Added setup step for bun to ensure the environment is correctly configured. * fix: update file upload access level in index.js - Modified the file upload function to include an access level parameter, setting it to 'public' for improved visibility of uploaded files. * chore: update GitHub Actions workflow to install dependencies with bun - Added steps to install dependencies using bun in the upload-to-blob.yml workflow. - Included setup for Node.js version 22 to ensure compatibility with the new dependency management.
1 parent fec3683 commit 34cae61

File tree

6 files changed

+337
-1
lines changed

6 files changed

+337
-1
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
name: Upload Files to Vercel Blob
2+
3+
on: [push]
4+
5+
jobs:
6+
upload-static-files:
7+
runs-on: ubuntu-latest
8+
name: Upload static files to Vercel Blob
9+
steps:
10+
- name: Checkout repository
11+
uses: actions/checkout@v4
12+
13+
- uses: oven-sh/setup-bun@v2
14+
15+
- uses: actions/checkout@v4
16+
- uses: actions/setup-node@v4
17+
with:
18+
node-version: 22
19+
20+
- name: Install dependencies
21+
run: bun install
22+
23+
- name: Create sample files
24+
run: |
25+
mkdir -p dist
26+
echo "Hello from GitHub Actions!" > dist/hello.txt
27+
echo "Build timestamp: $(date)" > dist/build-info.txt
28+
echo '{"version": "1.0.0", "build": "'$(date -u +%Y%m%d%H%M%S)'"}' > dist/metadata.json
29+
30+
- name: Upload hello.txt to Vercel Blob
31+
uses: ./
32+
with:
33+
source: "dist/hello.txt"
34+
destination: "uploads/hello.txt"
35+
read-write-token: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
36+
37+
- name: Upload build info with timestamp
38+
uses: ./
39+
with:
40+
source: "dist/build-info.txt"
41+
destination: "builds/build-${{ github.sha }}.txt"
42+
read-write-token: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
43+
44+
- name: Upload metadata JSON
45+
uses: ./
46+
with:
47+
source: "dist/metadata.json"
48+
destination: "metadata/build-${{ github.run_number }}.json"
49+
read-write-token: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
50+
51+
upload-build-artifacts:
52+
runs-on: ubuntu-latest
53+
name: Build and upload artifacts
54+
steps:
55+
- name: Checkout repository
56+
uses: actions/checkout@v4
57+
58+
- uses: oven-sh/setup-bun@v2
59+
60+
- uses: actions/checkout@v4
61+
- uses: actions/setup-node@v4
62+
with:
63+
node-version: 22
64+
65+
- name: Install dependencies
66+
run: bun install
67+
68+
- name: Build project
69+
run: |
70+
# Simulate a build process
71+
mkdir -p build
72+
bun run build || echo "No build script found, creating sample build files"
73+
echo "Built application at $(date)" > build/app.txt
74+
echo "Version: ${{ github.sha }}" >> build/app.txt
75+
76+
- name: Upload build artifact
77+
id: upload-build
78+
uses: ./
79+
with:
80+
source: "build/app.txt"
81+
destination: "releases/${{ github.ref_name }}/app-${{ github.sha }}.txt"
82+
read-write-token: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
83+
84+
- name: Display upload result
85+
run: |
86+
echo "Build artifact uploaded successfully!"
87+
echo "URL: ${{ steps.upload-build.outputs.url }}"
88+
89+
conditional-upload:
90+
runs-on: ubuntu-latest
91+
name: Conditional file upload
92+
if: github.event_name == 'workflow_dispatch' && github.event.inputs.file_to_upload != ''
93+
steps:
94+
- name: Checkout repository
95+
uses: actions/checkout@v4
96+
97+
- name: Check if file exists
98+
id: check-file
99+
run: |
100+
if [ -f "${{ github.event.inputs.file_to_upload }}" ]; then
101+
echo "file_exists=true" >> $GITHUB_OUTPUT
102+
echo "File exists: ${{ github.event.inputs.file_to_upload }}"
103+
else
104+
echo "file_exists=false" >> $GITHUB_OUTPUT
105+
echo "File not found: ${{ github.event.inputs.file_to_upload }}"
106+
fi
107+
108+
- name: Upload specified file
109+
if: steps.check-file.outputs.file_exists == 'true'
110+
uses: ./
111+
with:
112+
source: ${{ github.event.inputs.file_to_upload }}
113+
destination: "manual-uploads/${{ github.event.inputs.file_to_upload }}"
114+
read-write-token: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
115+
116+
upload-with-error-handling:
117+
runs-on: ubuntu-latest
118+
name: Upload with error handling
119+
steps:
120+
- name: Checkout repository
121+
uses: actions/checkout@v4
122+
123+
- name: Create test file
124+
run: |
125+
mkdir -p temp
126+
echo "Test file content" > temp/test.txt
127+
128+
- name: Upload with error handling
129+
id: upload-test
130+
continue-on-error: true
131+
uses: ./
132+
with:
133+
source: "temp/test.txt"
134+
destination: "tests/test-${{ github.run_id }}.txt"
135+
read-write-token: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
136+
137+
- name: Handle upload result
138+
run: |
139+
if [ "${{ steps.upload-test.outcome }}" == "success" ]; then
140+
echo "✅ Upload successful!"
141+
echo "File URL: ${{ steps.upload-test.outputs.url }}"
142+
else
143+
echo "❌ Upload failed!"
144+
echo "Please check your BLOB_READ_WRITE_TOKEN secret"
145+
fi

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.cursor

README.md

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,109 @@
1-
# vercel-blob-action
1+
# Vercel Blob Action
2+
3+
This GitHub Action allows you to upload files to Vercel Blob storage by specifying a source file and destination path. It provides an easy way to manage blob storage in your Vercel projects through GitHub Actions workflows.
4+
5+
## Inputs
6+
7+
### `source`
8+
9+
**Required** The source path of the file you want to upload to Vercel Blob storage.
10+
11+
### `destination`
12+
13+
**Required** The destination path where the file should be stored in Vercel Blob storage.
14+
15+
### `read-write-token`
16+
17+
**Required** Your Vercel Blob read-write token (`BLOB_READ_WRITE_TOKEN`). This should be stored as a GitHub secret for security.
18+
19+
## Outputs
20+
21+
### `url`
22+
23+
The URL of the uploaded blob file.
24+
25+
## Environment Variables
26+
27+
This action requires a Vercel Blob read-write token. The action will automatically set the `BLOB_READ_WRITE_TOKEN` environment variable for the Vercel Blob SDK to use. You can obtain this token from your Vercel dashboard:
28+
29+
1. Go to your Vercel dashboard
30+
2. Navigate to Storage → Blob
31+
3. Create or copy your `BLOB_READ_WRITE_TOKEN`
32+
4. Add it as a GitHub secret in your repository
33+
34+
## Usage
35+
36+
To use this action in your workflow, add it as a step in your GitHub Actions workflow file:
37+
38+
```yaml
39+
name: Upload to Vercel Blob
40+
41+
on: [push]
42+
43+
jobs:
44+
upload_blob:
45+
runs-on: ubuntu-latest
46+
steps:
47+
- name: Checkout code
48+
uses: actions/checkout@v4
49+
50+
- name: Upload file to Vercel Blob
51+
uses: your-username/vercel-blob-action@v1
52+
with:
53+
source: "path/to/your/file.txt"
54+
destination: "uploads/file.txt"
55+
read-write-token: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
56+
```
57+
58+
## Example
59+
60+
Here's a complete example that uploads a build artifact to Vercel Blob storage:
61+
62+
```yaml
63+
name: Build and Upload
64+
65+
on:
66+
push:
67+
branches: [main]
68+
69+
jobs:
70+
build-and-upload:
71+
runs-on: ubuntu-latest
72+
steps:
73+
- name: Checkout code
74+
uses: actions/checkout@v4
75+
76+
- name: Build project
77+
run: |
78+
# Your build commands here
79+
echo "Built file" > dist/output.txt
80+
81+
- name: Upload to Vercel Blob
82+
uses: your-username/vercel-blob-action@v1
83+
with:
84+
source: "dist/output.txt"
85+
destination: "builds/output-${{ github.sha }}.txt"
86+
read-write-token: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
87+
88+
- name: Display blob URL
89+
run: echo "File uploaded to ${{ steps.upload.outputs.url }}"
90+
```
91+
92+
## Setting up the Token
93+
94+
1. **Get your Vercel Blob token:**
95+
96+
- Visit your [Vercel dashboard](https://vercel.com/dashboard)
97+
- Go to Storage → Blob
98+
- Create or copy your `BLOB_READ_WRITE_TOKEN`
99+
100+
2. **Add the token to GitHub Secrets:**
101+
- Go to your GitHub repository
102+
- Navigate to Settings → Secrets and variables → Actions
103+
- Click "New repository secret"
104+
- Name: `BLOB_READ_WRITE_TOKEN`
105+
- Value: Your Vercel Blob token
106+
107+
## License
108+
109+
This project is licensed under the ISC License.

action.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: "Vercel Blob Action"
2+
description: "A GitHub Action to interact with Vercel Blob storage"
3+
inputs:
4+
source:
5+
description: "The source path for the blob file"
6+
required: true
7+
destination:
8+
description: "The destination path for the blob in Vercel Blob storage"
9+
required: true
10+
read-write-token:
11+
description: "Vercel Blob read-write token (BLOB_READ_WRITE_TOKEN)"
12+
required: true
13+
outputs:
14+
url:
15+
description: "The URL of the uploaded blob"
16+
runs:
17+
using: "node20"
18+
main: "src/index.js"
19+
branding:
20+
icon: "cloud-upload"
21+
color: "blue"

package.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "vercel-blob-action",
3+
"version": "1.0.0",
4+
"description": "A GitHub Action for uploading files to Vercel Blob storage",
5+
"main": "src/index.js",
6+
"type": "module",
7+
"scripts": {
8+
"test": "echo \"Error: no test specified\" && exit 1"
9+
},
10+
"keywords": [
11+
"github-action",
12+
"vercel",
13+
"blob",
14+
"storage",
15+
"upload"
16+
],
17+
"author": "",
18+
"license": "ISC",
19+
"dependencies": {
20+
"@actions/core": "^1.10.0",
21+
"@actions/github": "^5.0.0",
22+
"@vercel/blob": "^0.23.4"
23+
}
24+
}

src/index.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import * as core from '@actions/core';
2+
import fs from 'fs';
3+
import { put } from '@vercel/blob';
4+
5+
async function run() {
6+
try {
7+
const sourcePath = core.getInput('source', { required: true });
8+
const destinationPath = core.getInput('destination', { required: true });
9+
const token = core.getInput('read-write-token', { required: true });
10+
11+
if (!token) {
12+
throw new Error('Vercel Blob read-write token is required. Please set the read-write-token input with your BLOB_READ_WRITE_TOKEN.');
13+
}
14+
15+
if (!fs.existsSync(sourcePath)) {
16+
throw new Error(`Source file does not exist: ${sourcePath}`);
17+
}
18+
19+
core.info(`Uploading file from ${sourcePath} to ${destinationPath}`);
20+
21+
const fileStream = fs.createReadStream(sourcePath);
22+
23+
// Set the token as an environment variable for the Vercel Blob SDK
24+
process.env.BLOB_READ_WRITE_TOKEN = token;
25+
26+
const result = await put(destinationPath, fileStream, {
27+
access: 'public'
28+
});
29+
30+
core.info(`File uploaded successfully to ${result.url}`);
31+
core.setOutput('url', result.url);
32+
} catch (error) {
33+
core.setFailed(`Action failed: ${error.message}`);
34+
}
35+
}
36+
37+
run();

0 commit comments

Comments
 (0)