Option to change base URL in latest.json
I use a private Github repo for my app and want to host the binaries outside of Github (in my case Cloudflare R2).
I currently:
- Use this Github action to build the app and publish to my private repo's packages
- Transfer the directory with all assets to a directory named after the current version in R2
- Modify the links in latest.json manually to point to a public R2 URL, e.g.
https://releases.example.com/v0.2.0/App_universal.tar.gz - Upload
latest.jsonto R2
Currently, the generated latest.json file contains github URLs for the releases. I would like it to point to my host's path ( etc) instead (note this includes the version).
However, I am open to other options. Perhaps I should be not be using static files at all. Or perhaps, there should be an option to upload to S3-compatible stores instead of Github. Feel free to suggest something better.
Thanks for your request! ~~Since uploading to other targets than github is currently considered out of scope for this action i will mark this as wontfix for now.~~
I'd love to write the latest.json file into the job output like the build artifacts but that's not possible with how the file is (currently) getting generated. 🤔
Edit: Hmm, maybe providing a (potentionally undocumented) option for a base url wouldn't be too bad. Should be simple enough.
Thanks for the swift response.
I do think all options should be documented, Github Actions is difficult enough to use as it is.
That said, to prevent XY problems: my FR is I want to host my stuff elsewhere, and that part should be common (even if Cloudflare R2 may be niche). I do want to have a full CD pipeline eventually, but I don't expect this repo to anticipate all weird use cases, like mine.
My Actions knowledge is minimal, but here's an idea:
- Add an option to disable uploading artifacts to Github Packages
- Add an option for base URL
- For users like me: add a custom step after the Tauri action which uses
artifactPathsoutput to invoke a custom upload, using e.g. FTP, S3 etc
Would something like that make sense?
Uploading the packages to a release is optional if you're okay with the action not creating a github release at all. The problem is that if the action does not upload the packages, it won't be able to create a json file either because it needs to collect the packages across the different runners and it currently relies on the release for that.
At that point creating the json file from scratch yourself would probably be easier for you and us.
Ah, right. Multiple jobs makes it more complicated.
The main issue is that the artifact file names (of zip and tar files) are not deterministic within stable Tauri. They've changed in the past and might change in the future, or simply based on config changes (say MSI to NSIS).
As a result, both steps (uploading & referencing them for the updater) benefit from a single source of truth for CD purposes. So to be safe(r) I'm trusting latest.json for now (at least more than me hard-coding them). So, an optional base URL arg would be helpful. For now, I'm using sed to replace it:
sed -i -e 's#https:.*\/#https://releases.example.com/v0.2.0/#' latest.json
Maybe the action can output found artifacts names (or full paths) so any following steps can generate requires fields on their own?
it already does https://github.com/tauri-apps/tauri-action#outputs
I trigger a separate workflow when the tauri workflow completes. You can also dispatch an S3 upload (R2 in this case) manually by specifying the release tag. Useful as you don't need to rebuild if just the transfer fails.
This way you have the full release as all jobs in the previous workflow are complete.
Simply gh release download to download all assets, aws s3 cp to upload them, and sed to replace links in latest.json
name: 'Upload Release to R2'
on:
workflow_run:
workflows: ["Release Tauri App"]
types:
- completed
workflow_dispatch:
inputs:
release-tag:
description: 'Release tag (e.g., v1.0.0 or v1.0.0-pre)'
required: true
type: string
jobs:
upload-to-r2:
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
runs-on: ubuntu-latest
permissions:
contents: read
actions: read
steps:
- uses: actions/checkout@v4
- name: Get release and download assets
id: download-assets
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
# Manual trigger - use provided release tag
TAG_NAME="${{ github.event.inputs.release-tag }}"
echo "Manual trigger: Looking for release with tag: $TAG_NAME"
else
# Automatic trigger from workflow_run
echo "Automatic trigger: Finding release created by workflow run..."
# Get the commit SHA from the triggering workflow run
HEAD_SHA="${{ github.event.workflow_run.head_sha }}"
echo "Looking for release with commit SHA: $HEAD_SHA"
# Find the release that matches this commit SHA
RELEASE_DATA=$(gh api repos/${{ github.repository }}/releases | \
jq -r --arg sha "$HEAD_SHA" '.[] | select(.target_commitish == $sha)')
if [[ -z "$RELEASE_DATA" || "$RELEASE_DATA" == "null" ]]; then
echo "❌ No release found for commit SHA: $HEAD_SHA"
echo "This might indicate the release workflow failed or the release was created with a different commit."
exit 1
fi
TAG_NAME=$(echo "$RELEASE_DATA" | jq -r '.tag_name')
fi
echo "Found release: $TAG_NAME"
echo "tag-name=$TAG_NAME" >> $GITHUB_OUTPUT
# Create directory for assets
mkdir -p release-assets
# Download all assets for the release using GitHub CLI
echo "Downloading all assets for release $TAG_NAME..."
cd release-assets
if gh release download "$TAG_NAME" --repo ${{ github.repository }}; then
echo "✅ Successfully downloaded all assets"
# List downloaded files with sizes
echo "Downloaded files:"
ls -lah
else
echo "❌ Failed to download assets for release $TAG_NAME"
exit 1
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Configure AWS CLI for Cloudflare R2
run: |
aws configure set aws_access_key_id ${{ secrets.R2_ACCESS_KEY_ID }}
aws configure set aws_secret_access_key ${{ secrets.R2_SECRET_ACCESS_KEY }}
- name: Upload artifacts to Cloudflare R2
run: |
TAG_NAME="${{ steps.download-assets.outputs.tag-name }}"
for file in release-assets/*; do
if [[ -f "$file" ]]; then
filename=$(basename "$file")
# Skip latest.json - it will be handled separately
if [[ "$filename" == "latest.json" ]]; then
echo "Skipping $filename for separate processing"
continue
fi
echo "Uploading $filename to $TAG_NAME/"
aws s3 cp "$file" s3://${{ secrets.R2_BUCKET_PREFIX }}/$TAG_NAME/ --endpoint-url ${{ secrets.R2_ENDPOINT_URL }} --checksum-algorithm CRC32
fi
done
echo "✅ All artifacts uploaded to R2 successfully!"
- name: Process and upload latest.json
if: ${{ !contains(steps.download-assets.outputs.tag-name, 'pre') }}
run: |
TAG_NAME="${{ steps.download-assets.outputs.tag-name }}"
if [[ -f "release-assets/latest.json" ]]; then
echo "Processing latest.json..."
# Replace GitHub URLs with R2 URLs
sed "s|https://github.com/${{ github.repository }}/releases/download/[^/]*/|${{ secrets.R2_BASE_URL }}/$TAG_NAME/|g" \
release-assets/latest.json > release-assets/latest-processed.json
echo "Uploading latest.json to bucket root..."
aws s3 cp release-assets/latest-processed.json s3://${{ secrets.R2_BUCKET_PREFIX }}/latest.json --endpoint-url ${{ secrets.R2_ENDPOINT_URL }} --checksum-algorithm CRC32
echo "✅ latest.json processed and uploaded successfully!"
else
echo "⚠️ latest.json not found in release assets"
fi
how ya'll handle the weird file names (all kind of characters replaced with . by github)? Do you keep the github names?
if i were to add an option for this (still not fond of the idea) in v1 (so a breaking change) should the urls rather keep the original file name when there's a baseUrl set?
Hmm i'll put this on the backburner again. Since it'd be opt-in we can add it after v1 as well.
Another question would be how much customization we should offer, should it have a syntax similar to releaseAssetNamePattern? I think that'd only make sense without the filename modification since we'd may have to rename the files on disk for that to really make sense.