Skip to content

Deploy Program

Deploy Program #32

name: Deploy Program
on:
workflow_dispatch:
inputs:
git_ref:
description: Release tag (e.g. release/candy-machine-core@latest , release/[email protected]) or commit to deploy
required: true
type: string
default: release/candy-guard@latest
program:
description: Program (required if not a release tag)
required: false
type: choice
options:
- candy-guard
- candy-machine-core
cluster:
description: Cluster environment
required: true
default: devnet
type: choice
options:
- devnet
- mainnet-beta
- sonic-devnet
- sonic-testnet
- eclipse-mainnet
- eclipse-devnet
dry_run:
description: Dry run
required: false
type: boolean
default: false
env:
CACHE: true
jobs:
check_tag:
name: 'Check tag'
runs-on: ubuntu-latest
outputs:
program: ${{ steps.set_program.outputs.program }}
type: ${{ steps.set_program.outputs.type }}
steps:
- name: Check tag
id: set_program
run: |
if [[ "${{ inputs.git_ref }}" =~ ^release/candy-guard@* ]]; then
echo program="candy-guard" >> $GITHUB_OUTPUT
echo type="release" >> $GITHUB_OUTPUT
elif [[ "${{ inputs.git_ref }}" =~ ^release/candy-machine-core@* ]]; then
echo program="candy-machine-core" >> $GITHUB_OUTPUT
echo type="release" >> $GITHUB_OUTPUT
elif [[ "${{ inputs.git_ref }}" == "candy-machine-core" || "${{ inputs.git_ref }}" == "candy-guard" ]]; then
echo program="${{ inputs.program }}" >> $GITHUB_OUTPUT
echo type="ref" >> $GITHUB_OUTPUT
else
echo "Non-release tag and program not specified"
exit 1;
fi
build_programs:
name: Programs
uses: ./.github/workflows/build-programs.yml
secrets: inherit
needs: check_tag
if: needs.check_tag.outputs.type == 'ref'
with:
git_ref: ${{ inputs.git_ref }}
test_js:
name: JS client
needs: build_programs
uses: ./.github/workflows/test-js.yml
secrets: inherit
with:
git_ref: ${{ inputs.git_ref }}
deploy_program:
name: Program / Deploy
runs-on: ubuntu-latest
needs: [test_js, check_tag]
if: always() && (needs.test_js.result == 'success' || needs.test_js.result == 'skipped')
permissions:
contents: write
steps:
- name: Git checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.git_ref }}
- name: Load environment variables
run: cat .github/.env >> $GITHUB_ENV
- name: Install Rust
uses: metaplex-foundation/actions/install-rust@v1
with:
toolchain: ${{ env.RUST_VERSION }}
- name: Install Solana
uses: metaplex-foundation/actions/install-solana@v1
with:
version: ${{ env.DEPLOY_SOLANA_VERSION }}
cache: ${{ env.CACHE }}
- name: Install cargo-release
uses: metaplex-foundation/actions/install-cargo-release@v1
if: github.event.inputs.publish_crate == 'true'
with:
cache: ${{ env.CACHE }}
- name: Set RPC
run: |
# We do this if waterfall because github actions does not allow dynamic access to secrets
if [ "${{ inputs.cluster }}" == "devnet" ]; then
echo RPC=${{ secrets.DEVNET_RPC }} >> $GITHUB_ENV
elif [ "${{ inputs.cluster }}" == "mainnet-beta" ]; then
echo RPC=${{ secrets.MAINNET_RPC }} >> $GITHUB_ENV
elif [ "${{ inputs.cluster }}" == "sonic-devnet" ]; then
echo RPC=${{ secrets.SONIC_DEVNET_RPC }} >> $GITHUB_ENV
elif [ "${{ inputs.cluster }}" == "sonic-testnet" ]; then
echo RPC=${{ secrets.SONIC_TESTNET_RPC }} >> $GITHUB_ENV
elif [ "${{ inputs.cluster }}" == "eclipse-devnet" ]; then
echo RPC=${{ secrets.ECLIPSE_DEVNET_RPC }} >> $GITHUB_ENV
elif [ "${{ inputs.cluster }}" == "eclipse-testnet" ]; then
echo RPC=${{ secrets.ECLIPSE_TESTNET_RPC }} >> $GITHUB_ENV
elif [ "${{ inputs.cluster }}" == "eclipse-mainnet" ]; then
echo RPC=${{ secrets.ECLIPSE_MAINNET_RPC }} >> $GITHUB_ENV
fi
- name: Identify Program
run: |
if [[ "${{ inputs.cluster }}" == "sonic"* ]]; then
echo ${{ secrets.CORE_CANDY_MACHINE_SONIC_DEPLOY_KEY }} > ./deploy-key.json
elif [[ "${{ inputs.cluster }}" == "eclipse"* ]]; then
echo ${{ secrets.CORE_CANDY_MACHINE_ECLIPSE_DEPLOY_KEY }} > ./deploy-key.json
fi
if [ "${{ needs.check_tag.outputs.program }}" == "candy-guard" ]; then
if [ ! -e "./deploy-key.json" ]; then
echo ${{ secrets.CORE_CANDY_GUARD_DEPLOY_KEY }} > ./deploy-key.json
fi
echo ${{ secrets.CORE_CANDY_GUARD_ID }} > ./program-id.json
echo PROGRAM_NAME="mpl_core_candy_guard" >> $GITHUB_ENV
else
if [ ! -e "./deploy-key.json" ]; then
echo ${{ secrets.CORE_CANDY_MACHINE_CORE_DEPLOY_KEY }} > ./deploy-key.json
fi
echo ${{ secrets.CORE_CANDY_MACHINE_CORE_ID }} > ./program-id.json
echo PROGRAM_NAME="mpl_core_candy_machine_core" >> $GITHUB_ENV
fi
- name: Download Program Builds
uses: actions/download-artifact@v4
if: needs.check_tag.outputs.type == 'ref'
with:
name: program-builds-${{ inputs.git_ref }}
- name: Download release asset
uses: actions/github-script@v5
id: get_release
if: needs.check_tag.outputs.type == 'release'
with:
script: |
const tag = "${{ inputs.git_ref }}";
const assetName = "${{ env.PROGRAM_NAME }}.so";
// Fetch the release associated with the tag
const release = await github.rest.repos.getReleaseByTag({
owner: context.repo.owner,
repo: context.repo.repo,
tag: tag
});
if (release.status !== 200) {
throw new Error(`Failed to fetch release for tag ${tag}`);
}
// Find the specific asset by name
const asset = release.data.assets.find(asset => asset.name === assetName);
if (!asset) {
throw new Error(`Asset ${assetName} not found in release tagged ${tag}`);
}
return {
download_url: asset.url,
asset_name: asset.name
};
- name: Download the Selected Asset
if: needs.check_tag.outputs.type == 'release'
run: |
mkdir -p ${{ github.workspace }}/programs/.bin
curl -L -o ${{ github.workspace }}/programs/.bin/${{ steps.get_release.outputs.asset_name }} \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Accept: application/octet-stream" \
"${{ steps.get_release.outputs.download_url }}"
- name: Deploy Program
if: github.event.inputs.dry_run == 'false'
run: |
echo "Deploying ${{ needs.check_tag.outputs.program }} to ${{ inputs.cluster }}"
solana -v program deploy ./programs/.bin/${{ env.PROGRAM_NAME }}.so \
-u ${{ env.RPC }} \
--program-id ./program-id.json \
-k ./deploy-key.json \
--max-sign-attempts 100 \
--use-rpc
- name: Publish crate
working-directory: ./programs/${{ needs.check_tag.outputs.program }}/program
if: github.event.inputs.publish_crate == 'true' && github.event.inputs.cluster == 'mainnet-beta'
run: |
git stash
git config user.name "${{ env.COMMIT_USER_NAME }}"
git config user.email "${{ env.COMMIT_USER_EMAIL }}"
cargo login ${{ secrets.CRATES_TOKEN }}
cargo release ${{ inputs.bump }} --no-confirm --no-push --no-tag --execute
git reset --soft HEAD~1
git stash pop
- name: Create tag
uses: actions/github-script@v5
with:
script: |
github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'refs/tags/${{ needs.check_tag.outputs.program }}-${{ inputs.cluster }}',
sha: '${{ github.sha }}'
});