Deploy Program #37
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Deploy Program | |
on: | |
workflow_dispatch: | |
inputs: | |
git_ref: | |
description: Release tag (release/[email protected]) or commit to deploy | |
required: true | |
type: string | |
default: release/[email protected] | |
program: | |
description: Program | |
required: true | |
default: core | |
type: choice | |
options: | |
- 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: | | |
echo program="core" >> $GITHUB_OUTPUT | |
if [[ "${{ inputs.git_ref }}" =~ ^release/core@* ]]; then | |
echo type="release" >> $GITHUB_OUTPUT | |
else | |
echo type="ref" >> $GITHUB_OUTPUT | |
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_programs: | |
name: Test Programs | |
uses: ./.github/workflows/test-programs.yml | |
secrets: inherit | |
needs: [build_programs, check_tag] | |
if: needs.check_tag.outputs.type == 'ref' | |
with: | |
program_matrix: '["mpl-${{ inputs.program }}"]' | |
git_ref: ${{ inputs.git_ref }} | |
test_js: | |
name: JS client | |
needs: [build_programs, check_tag] | |
uses: ./.github/workflows/test-js-client.yml | |
secrets: inherit | |
if: needs.check_tag.outputs.type == 'ref' | |
with: | |
git_ref: ${{ inputs.git_ref }} | |
test_rust: | |
name: Rust client | |
needs: [build_programs, check_tag] | |
uses: ./.github/workflows/test-rust-client.yml | |
secrets: inherit | |
if: needs.check_tag.outputs.type == 'ref' | |
with: | |
git_ref: ${{ inputs.git_ref }} | |
deploy_program: | |
name: Program / Deploy | |
runs-on: ubuntu-latest-16-cores | |
needs: [check_tag, test_js, test_programs, test_rust] | |
if: | | |
always() | |
&& (needs.test_js.result == 'success' || needs.test_js.result == 'skipped') | |
&& (needs.test_programs.result == 'success' || needs.test_programs.result == 'skipped') | |
&& (needs.test_rust.result == 'success' || needs.test_rust.result == 'skipped') | |
permissions: | |
contents: write | |
steps: | |
- name: Git checkout | |
uses: actions/checkout@v4 | |
with: | |
token: ${{ secrets.SVC_TOKEN }} | |
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: | | |
echo PROGRAM_NAME="mpl_core" >> $GITHUB_ENV | |
echo ${{ secrets.CORE_ID }} > ./program-id.json | |
if [[ "${{ inputs.cluster }}" == "sonic"* ]]; then | |
echo ${{ secrets.CORE_SONIC_DEPLOY_KEY }} > ./deployer-key.json | |
echo DEPLOY_TYPE="direct" >> $GITHUB_ENV | |
elif [[ "${{ inputs.cluster }}" == "eclipse"* ]]; then | |
echo ${{ secrets.CORE_ECLIPSE_DEPLOY_KEY }} > ./deployer-key.json | |
echo DEPLOY_TYPE="direct" >> $GITHUB_ENV | |
elif [[ "${{ inputs.cluster }}" == "devnet" ]]; then | |
echo DEPLOY_TYPE="squads" >> $GITHUB_ENV | |
echo SQUADS_MULTISIG="Gs6jZWxXFvmZtBcyYr6fBXX5ikwRTemBDS4f6kFuB31U" >> $GITHUB_ENV | |
echo SQUADS_VAULT="Fsxr5WVKZZoeb7xgwTWRHymSRVGY9vk7m5B5GPu1KU59" >> $GITHUB_ENV | |
echo SQUADS_PROGRAM_INDEX="1" >> $GITHUB_ENV | |
elif [[ "${{ inputs.cluster }}" == "mainnet-beta" ]]; then | |
echo DEPLOY_TYPE="squads" >> $GITHUB_ENV | |
echo SQUADS_MULTISIG="Gs6jZWxXFvmZtBcyYr6fBXX5ikwRTemBDS4f6kFuB31U" >> $GITHUB_ENV | |
echo SQUADS_VAULT="Fsxr5WVKZZoeb7xgwTWRHymSRVGY9vk7m5B5GPu1KU59" >> $GITHUB_ENV | |
echo SQUADS_PROGRAM_INDEX="1" >> $GITHUB_ENV | |
else | |
echo "Invalid cluster: ${{ inputs.cluster }}" | |
exit 1 | |
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 }}_program.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}`); | |
} | |
const asset = release.data.assets.find(asset => asset.name === assetName); | |
if (!asset) { | |
throw new Error(`Asset ${assetName} not found in release tagged ${tag}`); | |
} | |
core.setOutput("url", asset.url); | |
- 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/${{ env.PROGRAM_NAME }}_program.so \ | |
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ | |
-H "Accept: application/octet-stream" \ | |
"${{ steps.get_release.outputs.url }}" | |
- name: Deploy Program | |
if: github.event.inputs.dry_run == 'false' && env.DEPLOY_TYPE == 'direct' | |
run: | | |
echo "Deploying ${{ needs.check_tag.outputs.program }} to ${{ inputs.cluster }}" | |
solana -v program deploy ./programs/.bin/${{ env.PROGRAM_NAME }}_program.so \ | |
-u ${{ env.RPC }} \ | |
--program-id ./program-id.json \ | |
-k ./deployer-key.json \ | |
--max-sign-attempts 100 \ | |
--use-rpc | |
rm ./deployer-key.json | |
rm ./program-id.json | |
- name: Deploy squads buffer | |
if: github.event.inputs.dry_run == 'false' && env.DEPLOY_TYPE == 'squads' | |
run: | | |
echo "Deploying buffer for ${{ inputs.program }} on ${{ inputs.cluster }}" | |
echo ${{ secrets.SQUADS_BOT_KEY }} > ./submitter-key.json | |
BUFFER=$(solana program write-buffer -u ${{ env.RPC }} -k ./submitter-key.json --max-sign-attempts 100 --use-rpc ./programs/.bin/${{ env.PROGRAM_NAME }}_program.so | awk '{print $2}') | |
echo "Buffer: $BUFFER" | |
solana program set-buffer-authority $BUFFER \ | |
--new-buffer-authority ${{ env.SQUADS_VAULT }} \ | |
-k ./submitter-key.json \ | |
-u ${{ env.RPC }} | |
rm ./submitter-key.json | |
echo "BUFFER=$BUFFER" >> $GITHUB_ENV | |
- name: Create Squads proposal | |
if: github.event.inputs.dry_run == 'false' && env.DEPLOY_TYPE == 'squads' | |
uses: metaplex-foundation/squads-program-upgrade@main | |
with: | |
network-url: ${{ env.RPC }} | |
program-multisig: ${{ env.SQUADS_MULTISIG }} | |
program-id: 'CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d' | |
program-index: ${{ env.SQUADS_PROGRAM_INDEX }} | |
buffer: ${{ env.BUFFER }} | |
spill-address: 'botTxAkJhuCtNNn9xsH8fHJjzTkcN6XD4dR3R5hkzV2' | |
authority: ${{ env.SQUADS_VAULT }} | |
name: 'Deploy ${{ inputs.git_ref }}' | |
keypair: ${{ secrets.SQUADS_BOT_KEY }} | |
- name: Create env tag | |
uses: actions/github-script@v7 | |
if: github.event.inputs.dry_run == 'false' | |
with: | |
github-token: ${{ secrets.GH_TAGGER_TOKEN }} | |
script: | | |
const refData = await github.rest.git.getRef({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
ref: 'tags/${{ inputs.git_ref }}' | |
}); | |
if (refData.status !== 200) { | |
throw new Error('Failed to fetch existing tag'); | |
} | |
await github.rest.git.createRef({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
ref: 'refs/tags/${{ needs.check_tag.outputs.program }}-${{ inputs.cluster }}', | |
sha: refData.data.object.sha | |
}).catch(err => { | |
if (err.status !== 422) throw err; | |
github.rest.git.updateRef({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
ref: 'tags/${{ needs.check_tag.outputs.program }}-${{ inputs.cluster }}', | |
sha: refData.data.object.sha | |
}); | |
}) |