I am learning Rust and writing some basic CLI tools as an exercise. I am storing my application source in Github, using Github actions to generate binaries and publish those binaries via Github releases.
The issue is; I am unsure how to cross compile my Rust application for various target architectures and operating systems.
(Apologise for the comparison) Previously when using Go, I could specify the target CPU architecture and target Operating System in the build command like:
env GOARCH=arm64 GOOS=darwin go build
When looking to see if there is an equivalent in Rust I am seeing instructions telling me to use virtualization and various other techniques to cross compile.
I suspect I might just be bad at researching, is there an equivalent simple way to cross compile Rust applications?
If not, why is that and could you point me to resources to help me learn how to do it?
cross makes this really easy, especially since it's supported by actions-rs/cargo.
I'm using something like
name: 'Release'
on:
push:
tags:
- 'v*'
env:
CARGO_INCREMENTAL: 0
jobs:
build:
name: Binary
strategy:
fail-fast: false
matrix:
job:
- { target: x86_64-unknown-linux-musl, exe: amd64-linux, os: ubuntu-latest }
- { target: aarch64-unknown-linux-musl, exe: aarch64-linux, os: ubuntu-latest }
- { target: armv7-unknown-linux-musleabi, exe: armv7-linux, os: ubuntu-latest }
- { target: wasm32-wasi, exe: wasi.wasm, os: ubuntu-latest }
- { target: x86_64-apple-darwin, exe: macos, os: macos-latest }
- { target: x86_64-pc-windows-msvc, exe: windows.exe, os: windows-2019 }
runs-on: ${{ matrix.job.os }}
steps:
- uses: actions/checkout#v2
- uses: actions-rs/toolchain#v1
with:
profile: minimal
toolchain: 1.62.0
override: true
target: ${{ matrix.job.target }}
components: rust-src # necessary for wasi, because there isn't a cross image for it
- uses: actions-rs/cargo#v1
with:
use-cross: true
args: --release --target=${{ matrix.job.target }} --locked
command: build
- name: Rename result
run: |
rm target/${{ matrix.job.target }}/release/name-of-binary.d
cp target/${{ matrix.job.target }}/release/name-of-binary* name-of-binary-${{ matrix.job.exe }}
- name: Archive production artifacts
uses: actions/upload-artifact#v2
with:
name: arty
path: name-of-binary-${{ matrix.job.exe }}
# Release artifacts in a separate step to make sure all are successfully produced and no partial release is created
on one of my projects.
I also specify
[profile.release]
lto = "fat"
strip = "debuginfo"
in my Cargo.toml to make the released files a bit nicer.
It is probably worth noting that Rust crates make it much easier to build and link in C/C++ libraries than Go, possibly invoking CMake or worse. Cross-compiling such crates can be a lot more difficult and how to do it exactly is up to the specific crate.
Related
I have a bunch of Github Actions that require exactly the same init process. I decided to implement a reusable workflow, but it turned out that the data is not shared between workflows.
I managed to solve that issue using actions/cache. I simply cache all the files with path: '**/**', but I have a feeling that it's not an optimal approach. It doesn't feel like actions/cache was meant to be used for sharing the entire build directory. It was rather designed for sharing things like node_modules or other generated files to save processing time.
The solution provided below works, but it feels hacky. I wonder is there a better solution that would not require caching (I know I could use artifacts but it also doesn't feel right).
That's what I ended up with:
# ./.github/workflows/init.yml
name: init
on: workflow_call
jobs:
init:
name: Init
runs-on: ubuntu-20.04
steps:
- name: Cache
uses: actions/cache#v3
with:
path: '**/**' # Cache all files
key: build-files-{{ github.sha }} # makes the key unique for each commit
- name: Checkout code
uses: actions/checkout#v2
with:
token: ${{ secrets.NPM_TOKEN }}
- name: Install Node
uses: actions/setup-node#v2
with:
always-auth: true
node-version: 12.x
cache: 'npm'
registry-url: 'https://npm.pkg.github.com'
scope: '#my-company-namespace'
- name: Install NPM packages
run: npm install
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
- name: Download translations
run: npm run translations
env:
I18N_PROJECT_ID: ${{secrets.I18N_PROJECT_ID}}
I18N_API_KEY: ${{secrets.I18N_API_KEY}}
# ./.github/workflows/linter.yml
name: Linter
on: [push]
jobs:
init:
secrets: inherit
uses: ./.github/workflows/init.yml # re-use the init.yml workflow
linter:
runs-on: ubuntu-20.04
needs: [init]
steps:
- name: Cache
uses: actions/cache#v3
with:
path: '**/**' # restore cached files
key: build-files-{{ github.sha }}
- name: Linter
run: npm run lint
I have a GitHub action doing .net solution build, test and deploy to Azure:
name: Build and deploy ASP.Net Core app to Azure Web App - project-test-api
on:
push:
branches:
- main
workflow_dispatch:
env:
# Stop wasting time caching packages
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
# Disable sending usage data to Microsoft
DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: AutoModality/action-clean#v1
- uses: actions/checkout#v2
- name: Set up .NET Core
uses: actions/setup-dotnet#v1
with:
dotnet-version: '6.0.x'
include-prerelease: true
- name: Manually restore
working-directory: SolutionDir
run: dotnet restore --force
- name: Build with dotnet
working-directory: SolutionDir
run: dotnet build --configuration Release --no-restore
- name: Test
working-directory: SolutionDir
run: dotnet test --no-restore --no-build --configuration Release
- name: dotnet publish
working-directory: SolutionDir
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp
- name: Upload artifact for deployment job
uses: actions/upload-artifact#v2
with:
name: .net-app
path: ${{env.DOTNET_ROOT}}/myapp
deploy:
runs-on: ubuntu-latest
needs: build
environment:
name: 'Production'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
steps:
- name: Download artifact from build job
uses: actions/download-artifact#v2
with:
name: .net-app
- name: Deploy to Azure Web App
id: deploy-to-webapp
uses: azure/webapps-deploy#v2
with:
app-name: 'quickplanner-test-api'
slot-name: 'Production'
publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_SECRET }}
package: .
Recently I added a test project to the solution. From what I can see one of the packages used in the test project uses Newtonsoft.Json v9.0 when the rest of the solution uses v13.0. The solution can be built locally, tested and everything is ok. The GitHub action also finishes successfully building the solution, runs tests and deploys it to Azure. The problem occurs on Azure - somewhere along the way GitHub action uses an older version of Newtonsoft.Json. All projects expect newer ones, so the whole website breaks because of this. I'm not sure how to fix this - I've tried adding manually correct version of Newtonsoft.Json to the test project, to all projects, clearing caches in GitHub actions but without luck. What works is just removing test project from the solution, but obviously I want tests to be working. Does anyone has idea why this breaks and how to fix it?
I managed to fix this problem by adding this code to my Tests project csproj file:
<PropertyGroup>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>
I'm not entirely sure what was the root cause behind this problem
I've my anchor project build with serum's anchor framework.
I want to set up github action, So that whenever a new pull request is raised or any commit is made on the main branch, I can be sure that no code has been broken and flag any such pull request.
Here is what I've tried. But it needs around 18 to 20 minutes to run and still unsuccessful.
name: Rust
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v3
- name: Install AVM
run: cargo install --git https://github.com/project-serum/anchor avm --force
- name: Install Anchor
run: avm install 0.24.2 && avm use 0.24.2
- name: Build
run: anchor build
- name: Run tests
run: anchor test
The initial approach was not efficient
cons:
need many manual installations of solana, nvm, node and yarn
hence too much time for a run
complex
reinventing wheel
When dug deep found out that found out that serum releases docker images for this purpose only. So I modified my action file to
name: Rust
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
container: projectserum/build:v0.24.2
steps:
- uses: actions/checkout#v3
- name: List rustup toolchains
run: rustup toolchain list
- name: Set default toolchain
run: rustup default stable
- name: List rustup toolchains
run: rustup toolchain list
- name: Generate new keygen
run: solana-keygen new
- name: Set solana target cluster to local
run: solana config set --url http:localhost:8899
- name: Check solana config
run: solana config get
- name: Install yarn dependencies
run: yarn install
- name: Build
run: anchor build
- name: Run tests
run: anchor test
It brought down the run time from 18 minutes to 3 minutes approximately.
References:
https://github.com/project-serum/anchor/tree/master/docker
https://github.com/yourarj/solana-twitter-enhanced/blob/main/.github/workflows/rust.yml
I am trying to deploy a static web app using Azure services with Github Actions.
The problem is, I need to run a script using NodeJS. I am using ESM modules which are working fine in Node v14. The problem is, during build task Azure (or github) uses v12.8 (LTS I guess) where ESM modules are not supported.
name: Azure Static Web Apps CI/CD
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- master
jobs:
build_and_deploy_job:
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
name: Build and Deploy Job
steps:
- uses: actions/checkout#v2
with:
submodules: true
- name: Build And Deploy
id: builddeploy
uses: Azure/static-web-apps-deploy#v0.0.1-preview
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_LEMON_GLACIER_0C510BD03 }}
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
action: "upload"
###### Repository/Build Configurations - These values can be configured to match you app requirements. ######
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
app_location: "/" # App source code path
api_location: "api" # Api source code path - optional
app_artifact_location: "public" # Built app content directory - optional
###### End of Repository/Build Configurations ######
close_pull_request_job:
if: github.event_name == 'pull_request' && github.event.action == 'closed'
runs-on: ubuntu-latest
name: Close Pull Request Job
steps:
- name: Close Pull Request
id: closepullrequest
uses: Azure/static-web-apps-deploy#v0.0.1-preview
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_LEMON_GLACIER_0C510BD03 }}
action: "close"
I was trying to specify the Node version using this step:
- name: Setup Node 14.x
uses: actions/setup-node#v1
with:
node-version: '14.x'
Still, the build process uses 12.8.
What am I doing wrong and is it possible to use a specific version in my case?
For those like me who came to this question well after 12.8 (and indeed 14.x) were considered "legacy" releases, and yet static-web-apps-deploy, even at v1, continues to use an old version of Node (in my case, 14.19.1), I found the answer on a blog post by Edi Wang. It says to add an "engine" property to your package.json, with a "node" property specifying the version you need. Since I wanted v16 or above, I added the following:
"engines": {
"node": ">=16.0.0"
},
This caused Oryx to find and download Node v17.6.0:
Detecting platforms...
Detected following platforms:
nodejs: 17.6.0
Version '17.6.0' of platform 'nodejs' is not installed. Generating script to install it...
Documentation says that supported Node versions are only as high as LTS. 12.8 currently. You can't use the Azure/static-web-apps-deploy#v0.0.1-preview action with v14.x right now.
I solved the problem by using github-actions and pushing the build to the azure storage.
on:
push:
branches:
- master
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout#master
- name: Install Dependencies
run: npm install
- name: Build
run: npm run build
- name: Archive Production Artifact
uses: actions/upload-artifact#master
with:
name: build
path: build
deploy:
name: Deploy
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout#master
- name: Download Artifact
uses: actions/download-artifact#master
with:
name: build
- name: Deploy to Firebase
uses: w9jds/firebase-action#master
with:
args: deploy --only hosting
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
now this is the gtihub actions workflow it is executing build job without errors but in deployment there comes an error
this is the error image
the error its shows is Error: Specified public directory 'build' does not exist, can't deploy hosting to site landing-page-design-1 i have followed the blog from where the workflow is copied i did everything same except some of my project details which is obvious please help me out why is this error occuring and how can i fix it
You're probably unpacking artifact to root directory instead of build/.
I'm guessing article was written for download-artifact#v1 while you are using download-artifact#v2 (as that's where master points currently). Difference between both is discussed here.
I'd verify first what is going on after artifact is downloaded
- name: Display directory structure
run: ls -R
shell: bash
If files are indeed in root directory, adding path should fix that.
- name: Download Artifact
uses: actions/download-artifact#v2
with:
name: build
path: build
PS: Using actions/<name>#master is not recommended, as it can always lead to issues if same action behaves differently between versions... for example actions/download-artifact ;)
You can also try to use firebase-publish-react to simplify your workflow file
This particular action plugin takes care of building the application internally and also can reuse the build directory from previous steps.
- name: Deploy to Firebase
uses: mohammed-atif/firebase-publish-react#v1.0
with:
firebase-token: ${{ secrets.FIREBASE_TOKEN }}