Production-ready GitHub Action for uploading files to Google Drive with intelligent deduplication, update-in-place, and comprehensive sharing controls.
- π Multi-file support - Upload via glob patterns or newline-separated lists
- π MD5 deduplication - Skip uploads when content unchanged
- β»οΈ Update-in-place - Preserve permissions and links on overwrite
- π Security hardened - Token masking, jq-based JSON, credential cleanup
- π Flexible sharing - None, anyone, domain, or specific user/email
- π Rich outputs - File IDs, links, update/skip status
- π‘οΈ Robust error handling - Retries, timeouts, preflight checks
- π¦ Canonical links - Direct from Drive API (no brittle URL hacks)
- π’ Shared Drive support - Full compatibility with Google Workspace Shared Drives
- name: Upload to Google Drive
uses: code-atlantic/[email protected]
with:
filename: my-plugin.zip
credentials: ${{ secrets.GOOGLE_DRIVE_CREDENTIALS_B64 }}
folder_id: ${{ secrets.GOOGLE_DRIVE_FOLDER_ID }}- name: Upload and share publicly
uses: code-atlantic/[email protected]
with:
filename: release-package.zip
credentials: ${{ secrets.GOOGLE_DRIVE_CREDENTIALS_B64 }}
folder_id: ${{ secrets.GOOGLE_DRIVE_FOLDER_ID }}
sharing: anyone
link_discoverable: false # Link-only, not searchable- name: Upload all release artifacts
uses: code-atlantic/[email protected]
with:
filename: 'dist/*.zip'
credentials: ${{ secrets.GOOGLE_DRIVE_CREDENTIALS_B64 }}
folder_id: ${{ secrets.GOOGLE_DRIVE_FOLDER_ID }}
overwrite: true| Input | Required | Default | Description |
|---|---|---|---|
filename |
β Yes | - | File(s) to upload. Accepts globs (e.g., dist/*.zip) or newline-separated list |
credentials |
β Yes | - | Base64-encoded Google Service Account credentials JSON |
folder_id |
β Yes | - | Google Drive folder ID to upload to |
overwrite |
No | true |
Overwrite existing files with same name |
sharing |
No | none |
Sharing mode: none, anyone, domain, specific |
sharing_role |
No | reader |
Permission role: reader, writer, commenter |
sharing_email |
No | - | Email address for specific sharing mode |
sharing_domain |
No | - | Domain for domain sharing mode |
link_discoverable |
No | false |
For sharing=anyone: allow file discovery in search |
| Output | Description |
|---|---|
file_id |
Google Drive file ID |
file_name |
Uploaded filename |
web_view_link |
Google Drive web view link (https://drive.google.com/file/d/...) |
download_link |
Canonical download link (Drive API webContentLink) |
direct_link |
Direct download link (same as download_link) |
updated |
Whether file was updated (true) vs created (false) - false if skipped |
skipped |
Whether upload was skipped due to identical MD5 (true) or not (false) |
- Go to Google Cloud Console
- Create a new project or select existing
- Enable Google Drive API
- Create Service Account credentials
- Download JSON key file
For Personal Drive:
- Create a folder in Google Drive
- Right-click β Share β Add the service account email
- Grant Editor permissions
- Copy the folder ID from URL:
https://drive.google.com/drive/folders/FOLDER_ID_HERE
For Shared Drive (Google Workspace):
- Open your Shared Drive
- Right-click β Manage members β Add the service account email
- Grant Content Manager or Manager permissions
- Create or select a folder, copy folder ID from URL
Encode credentials and add to repository secrets:
# Encode service account JSON to base64
cat service-account.json | base64 | pbcopy # macOS
cat service-account.json | base64 -w 0 # Linux
# Add to GitHub Secrets:
# - GOOGLE_DRIVE_CREDENTIALS_B64 (paste base64 string)
# - GOOGLE_DRIVE_FOLDER_ID (paste folder ID)name: Release Plugin
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build release package
run: npm run build:production
- name: Upload to Google Drive
id: drive_upload
uses: code-atlantic/[email protected]
with:
filename: dist/my-plugin_${{ github.ref_name }}.zip
credentials: ${{ secrets.GOOGLE_DRIVE_CREDENTIALS_B64 }}
folder_id: ${{ secrets.GOOGLE_DRIVE_FOLDER_ID }}
sharing: anyone
- name: Use download link
run: |
echo "Download: ${{ steps.drive_upload.outputs.download_link }}"
echo "View: ${{ steps.drive_upload.outputs.web_view_link }}"- name: Upload only if changed
id: upload
uses: code-atlantic/[email protected]
with:
filename: my-file.zip
credentials: ${{ secrets.GOOGLE_DRIVE_CREDENTIALS_B64 }}
folder_id: ${{ secrets.GOOGLE_DRIVE_FOLDER_ID }}
- name: Notify only if updated
if: steps.upload.outputs.updated == 'true'
run: echo "File was updated!"
- name: Skip notification if identical
if: steps.upload.outputs.skipped == 'true'
run: echo "File unchanged, skipped upload"- name: Share with organization
uses: code-atlantic/[email protected]
with:
filename: internal-release.zip
credentials: ${{ secrets.GOOGLE_DRIVE_CREDENTIALS_B64 }}
folder_id: ${{ secrets.GOOGLE_DRIVE_FOLDER_ID }}
sharing: domain
sharing_domain: yourcompany.com
sharing_role: reader- name: Share with specific user
uses: code-atlantic/[email protected]
with:
filename: confidential.zip
credentials: ${{ secrets.GOOGLE_DRIVE_CREDENTIALS_B64 }}
folder_id: ${{ secrets.GOOGLE_DRIVE_FOLDER_ID }}
sharing: specific
sharing_email: [email protected]
sharing_role: writerβ
Token Masking - Access tokens masked in logs with ::add-mask::
β
jq-based JSON - All JSON built with jq (no injection vulnerabilities)
β
Credential Cleanup - Sensitive vars unset on script exit
β
Safe Glob Expansion - Proper nullglob/dotglob handling
β
Input Validation - All parameters validated before use
β
Quote-safe Search - Parent-based search with local filtering
- Least Privilege: Default
sharing: none- opt-in for public links - Service Account Scoping: Limit Drive API scope to specific folders
- Secret Rotation: Regularly rotate service account keys
- Audit Logs: Monitor Drive activity logs for anomalies
- MD5 Deduplication: Skip uploads when content identical (saves bandwidth)
- Cached File Search: Parse file metadata once per file (3x β 1x jq calls)
- Update-in-place: Preserve permissions/links vs delete+create
- Retry Logic: 5 retries with exponential backoff for transient failures
- Timeout Protection: 300s max per request prevents hanging
- Batch Operations: Efficient multi-file processing
- Bash 3.2+ Compatible: Portable array handling (works on macOS and all CI runners)
- Unchanged file: ~2s (MD5 check, skip upload)
- New file upload: ~5-10s (depending on size)
- Update existing: ~6-12s (resumable session + update)
β "Invalid credentials format"
- Ensure credentials are base64-encoded correctly
- Verify JSON structure with
echo $CREDS | base64 -d | jq .
β "folder_id is not a folder or not accessible"
- Verify folder ID is correct
- Ensure service account has Editor permissions on folder
- For Shared Drives: Add service account as member with Content Manager/Manager role
- For Shared Drives: Ensure
supportsAllDrives=trueis set (automatic in v0.3.0+)
β "No files matched: dist/*.zip"
- Check glob pattern matches files in workspace
- Verify files exist before upload step
β "Failed to get access token"
- Check service account key is valid
- Ensure Drive API is enabled in project
Enable debug output:
- name: Upload with debug
uses: code-atlantic/[email protected]
with:
filename: my-file.zip
credentials: ${{ secrets.GOOGLE_DRIVE_CREDENTIALS_B64 }}
folder_id: ${{ secrets.GOOGLE_DRIVE_FOLDER_ID }}
env:
ACTIONS_STEP_DEBUG: true- name: Upload to Drive
id: drive
uses: code-atlantic/[email protected]
with:
filename: release.zip
credentials: ${{ secrets.GOOGLE_DRIVE_CREDENTIALS_B64 }}
folder_id: ${{ secrets.GOOGLE_DRIVE_FOLDER_ID }}
sharing: anyone
- name: Notify Slack
run: |
curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
-H 'Content-Type: application/json' \
-d '{
"text": "π Release ${{ github.ref_name }} ready!",
"attachments": [{
"color": "good",
"fields": [
{"title": "Download", "value": "${{ steps.drive.outputs.download_link }}"},
{"title": "View", "value": "${{ steps.drive.outputs.web_view_link }}"}
]
}]
}'Contributions welcome! Please:
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing) - Open Pull Request
See CHANGELOG.md for version history.
MIT License - see LICENSE file for details.
Developed by Code Atlantic for the WordPress plugin release ecosystem.
Based on Google Drive API best practices and community feedback.
β If this action helped you, consider starring the repo!