diff --git a/.github/workflows/deploy-devnet.yml b/.github/workflows/deploy-devnet.yml new file mode 100644 index 00000000..a95a3df0 --- /dev/null +++ b/.github/workflows/deploy-devnet.yml @@ -0,0 +1,175 @@ +name: Deploy - Devnet + + +on: + workflow_dispatch: + inputs: + codex_image: + description: codexstorage/nim-codex:latest-dist-tests + required: false + type: string + workflow_call: + inputs: + codex_image: + description: codexstorage/nim-codex:latest-dist-tests + required: true + type: string + +env: + CODEX_NAMESPACE: codex + TOOLS_NAMESPACE: common + KUBE_CONFIG: ${{ secrets.DEVNET_KUBE_CONFIG }} + KUBE_VERSION: v1.33.1 + CODEX_IMAGE: ${{ inputs.codex_image }} + SSH_HOSTS: ${{ secrets.DEVNET_SSH_HOSTS }} + SSH_PORT: ${{ secrets.DEVNET_SSH_PORT }} + SSH_USERNAME: ${{ secrets.DEVNET_SSH_USERNAME }} + SSH_PRIVATE_KEY: ${{ secrets.DEVNET_SSH_KEY }} + + +jobs: + deploy-contracts: + name: Deploy contracts + runs-on: ubuntu-latest + steps: + - name: Create access token + uses: actions/create-github-app-token@v2 + id: app-token + with: + app-id: ${{ secrets.DEPLOYER_APP_ID }} + private-key: ${{ secrets.DEPLOYER_PRIVATE_KEY }} + repositories: codex-contracts-eth + + - name: Checkout sources + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Get contracts submodule ref + id: contracts + run: echo "ref=$(git rev-parse HEAD:vendor/codex-contracts-eth)" >> $GITHUB_OUTPUT + + - name: Deploy smart contracts + uses: the-actions-org/workflow-dispatch@v4 + with: + repo: codex-storage/codex-contracts-eth + workflow: devnet-contracts.yml + token: ${{ steps.app-token.outputs.token }} + wait-for-completion-timeout: 20m + wait-for-completion-interval: 20s + inputs: '{ "network": "codex_devnet", "contracts_ref": "${{ steps.contracts.outputs.ref }}" }' + + + bootstrap-nodes: + name: Bootstrap nodes + runs-on: ubuntu-latest + needs: deploy-contracts + steps: + - name: Codex Bootstrap - Update + uses: appleboy/ssh-action@v1 + with: + host: ${{ secrets.DEVNET_SSH_HOSTS }} + username: ${{ secrets.DEVNET_SSH_USERNAME }} + key: ${{ secrets.DEVNET_SSH_KEY }} + port: ${{ secrets.DEVNET_SSH_PORT }} + script: /opt/codex/remote-deploy.sh ${{ env.CODEX_IMAGE }} + + cluster-nodes: + name: Cluster nodes + runs-on: ubuntu-latest + needs: bootstrap-nodes + steps: + - name: Kubectl - Install ${{ env.KUBE_VERSION }} + uses: azure/setup-kubectl@v4 + with: + version: ${{ env.KUBE_VERSION }} + + - name: Kubectl - Kubeconfig + run: | + mkdir -p "${HOME}"/.kube + echo "${{ env.KUBE_CONFIG }}" | base64 -d > "${HOME}"/.kube/config + + - name: Codex Storage - Update + run: | + for node in {1..5}; do + kubectl -n "${{ env.CODEX_NAMESPACE }}" patch statefulset codex-storage-${node} \ + --patch '{"spec": {"template": {"spec":{"containers":[{"name": "codex", "image":"${{ env.CODEX_IMAGE }}"}]}}}}' + done + + - name: Codex Validators - Update + run: | + for node in {1..1}; do + kubectl -n "${{ env.CODEX_NAMESPACE }}" patch statefulset codex-validator-${node} \ + --patch '{"spec": {"template": {"spec":{"containers":[{"name": "codex", "image":"${{ env.CODEX_IMAGE }}"}]}}}}' + done + + - name: Codex Storage - Status + run: | + WAIT=300 + SECONDS=0 + sleep=1 + for instance in {1..5}; do + while (( SECONDS < WAIT )); do + pod=codex-storage-${instance}-1 + phase=$(kubectl get pod "${pod}" -n "${{ env.CODEX_NAMESPACE }}" -o jsonpath='{.status.phase}') + if [[ "${phase}" == "Running" ]]; then + echo "Pod ${pod} is in the ${phase} state" + break + else + echo "Pod ${pod} is in the ${phase} state - Check in ${sleep} second(s) / $((WAIT - SECONDS))" + fi + sleep "${sleep}" + done + done + + - name: Codex Validators - Status + run: | + WAIT=300 + SECONDS=0 + sleep=1 + for instance in {1..1}; do + while (( SECONDS < WAIT )); do + pod=codex-validator-${instance}-1 + phase=$(kubectl get pod "${pod}" -n "${{ env.CODEX_NAMESPACE }}" -o jsonpath='{.status.phase}') + if [[ "${phase}" == "Running" ]]; then + echo "Pod ${pod} is in the ${phase} state" + break + else + echo "Pod ${pod} is in the ${phase} state - Check in ${sleep} second(s) / $((WAIT - SECONDS))" + fi + sleep "${sleep}" + done + done + + - name: Tools - Update + run: | + crawler_pod=$(kubectl get pod -n "${{ env.TOOLS_NAMESPACE }}" -l 'app.kubernetes.io/name=crawler' -ojsonpath='{.items[0].metadata.name}' 2>/dev/null || true) + discordbot_pod=$(kubectl get pod -n "${{ env.TOOLS_NAMESPACE }}" -l 'app=discordbot' -ojsonpath='{.items[0].metadata.name}' 2>/dev/null || true) + + for pod in "${crawler_pod}" "${discordbot_pod}"; do + if [[ -n "${pod}" ]]; then + kubectl delete pod -n "${{ env.TOOLS_NAMESPACE }}" "${pod}" --grace-period=10 + fi + done + + - name: Tools - Status + run: | + WAIT=300 + SECONDS=0 + sleep=1 + crawler_pod=$(kubectl get pod -n "${{ env.TOOLS_NAMESPACE }}" -l 'app.kubernetes.io/name=crawler' -ojsonpath='{.items[0].metadata.name}' 2>/dev/null || true) + discordbot_pod=$(kubectl get pod -n "${{ env.TOOLS_NAMESPACE }}" -l 'app=discordbot' -ojsonpath='{.items[0].metadata.name}' 2>/dev/null || true) + for pod in "${crawler_pod}" "${discordbot_pod}"; do + if [[ -n "${pod}" ]]; then + while (( SECONDS < WAIT )); do + phase=$(kubectl get pod "${pod}" -n "${{ env.TOOLS_NAMESPACE }}" -o jsonpath='{.status.phase}') + if [[ "${phase}" == "Running" ]]; then + echo "Pod ${pod} is in the ${phase} state" + break + else + echo "Pod ${pod} is in the ${phase} state - Check in ${sleep} second(s) / $((WAIT - SECONDS))" + fi + sleep "${sleep}" + done + fi + done diff --git a/.github/workflows/docker-dist-tests.yml b/.github/workflows/docker-dist-tests.yml index 74bd84af..f6bb163c 100644 --- a/.github/workflows/docker-dist-tests.yml +++ b/.github/workflows/docker-dist-tests.yml @@ -13,6 +13,7 @@ on: - '.github/**' - '!.github/workflows/docker-dist-tests.yml' - '!.github/workflows/docker-reusable.yml' + - '!.github/workflows/deploy-devnet.yml' - 'docker/**' - '!docker/codex.Dockerfile' - '!docker/docker-entrypoint.sh' @@ -23,6 +24,11 @@ on: required: false type: boolean default: false + deploy_devnet: + description: Deploy Devnet + required: false + type: boolean + default: false jobs: @@ -40,6 +46,7 @@ jobs: run: | hash=$(git rev-parse --short HEAD:vendor/codex-contracts-eth) echo "hash=$hash" >> $GITHUB_OUTPUT + build-and-push: name: Build and Push uses: ./.github/workflows/docker-reusable.yml @@ -53,3 +60,12 @@ jobs: contract_image: "codexstorage/codex-contracts-eth:sha-${{ needs.get-contracts-hash.outputs.hash }}-dist-tests" run_release_tests: ${{ inputs.run_release_tests }} secrets: inherit + + deploy-devnet: + name: Deploy Devnet + uses: ./.github/workflows/deploy-devnet.yml + needs: build-and-push + if: ${{ inputs.deploy_devnet || github.event_name == 'push' && github.ref_name == github.event.repository.default_branch }} + with: + codex_image: ${{ needs.build-and-push.outputs.codex_image }} + secrets: inherit diff --git a/.github/workflows/docker-reusable.yml b/.github/workflows/docker-reusable.yml index e74927b4..eb614216 100644 --- a/.github/workflows/docker-reusable.yml +++ b/.github/workflows/docker-reusable.yml @@ -68,6 +68,10 @@ on: description: Specifies compatible smart contract image required: false type: string + outputs: + codex_image: + description: Codex Docker image tag + value: ${{ jobs.publish.outputs.codex_image }} env: @@ -91,15 +95,16 @@ env: jobs: + # Compute variables compute: - name: Compute build ID - runs-on: ubuntu-latest - outputs: - build_id: ${{ steps.build_id.outputs.build_id }} - steps: - - name: Generate unique build id - id: build_id - run: echo "build_id=$(openssl rand -hex 5)" >> $GITHUB_OUTPUT + name: Compute build ID + runs-on: ubuntu-latest + outputs: + build_id: ${{ steps.build_id.outputs.build_id }} + steps: + - name: Generate unique build id + id: build_id + run: echo "build_id=$(openssl rand -hex 5)" >> $GITHUB_OUTPUT # Build platform specific image build: @@ -134,7 +139,7 @@ jobs: run: | # Create contract label for compatible contract image if specified if [[ -n "${{ env.CONTRACT_IMAGE }}" ]]; then - echo "CONTRACT_LABEL=storage.codex.nim-codex.blockchain-image=${{ env.CONTRACT_IMAGE }}" >>$GITHUB_ENV + echo "CONTRACT_LABEL=storage.codex.nim-codex.blockchain-image=${{ env.CONTRACT_IMAGE }}" >> $GITHUB_ENV fi - name: Docker - Meta @@ -189,35 +194,35 @@ jobs: runs-on: ubuntu-latest outputs: version: ${{ steps.meta.outputs.version }} + codex_image: ${{ steps.image_tag.outputs.codex_image }} needs: [build, compute] steps: - - name: Docker - Variables run: | - # Adjust custom suffix when set and + # Adjust custom suffix when set if [[ -n "${{ env.TAG_SUFFIX }}" ]]; then - echo "TAG_SUFFIX=-${{ env.TAG_SUFFIX }}" >>$GITHUB_ENV + echo "TAG_SUFFIX=-${{ env.TAG_SUFFIX }}" >> $GITHUB_ENV fi # Disable SHA tags on tagged release if [[ ${{ startsWith(github.ref, 'refs/tags/') }} == "true" ]]; then - echo "TAG_SHA=false" >>$GITHUB_ENV + echo "TAG_SHA=false" >> $GITHUB_ENV fi # Handle latest and latest-custom using raw if [[ ${{ env.TAG_SHA }} == "false" ]]; then - echo "TAG_LATEST=false" >>$GITHUB_ENV - echo "TAG_RAW=true" >>$GITHUB_ENV + echo "TAG_LATEST=false" >> $GITHUB_ENV + echo "TAG_RAW=true" >> $GITHUB_ENV if [[ -z "${{ env.TAG_SUFFIX }}" ]]; then - echo "TAG_RAW_VALUE=latest" >>$GITHUB_ENV + echo "TAG_RAW_VALUE=latest" >> $GITHUB_ENV else - echo "TAG_RAW_VALUE=latest-{{ env.TAG_SUFFIX }}" >>$GITHUB_ENV + echo "TAG_RAW_VALUE=latest-{{ env.TAG_SUFFIX }}" >> $GITHUB_ENV fi else - echo "TAG_RAW=false" >>$GITHUB_ENV + echo "TAG_RAW=false" >> $GITHUB_ENV fi # Create contract label for compatible contract image if specified if [[ -n "${{ env.CONTRACT_IMAGE }}" ]]; then - echo "CONTRACT_LABEL=storage.codex.nim-codex.blockchain-image=${{ env.CONTRACT_IMAGE }}" >>$GITHUB_ENV + echo "CONTRACT_LABEL=storage.codex.nim-codex.blockchain-image=${{ env.CONTRACT_IMAGE }}" >> $GITHUB_ENV fi - name: Docker - Download digests @@ -257,9 +262,12 @@ jobs: docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf '${{ env.DOCKER_REPO }}@sha256:%s ' *) + - name: Docker - Image tag + id: image_tag + run: echo "codex_image=${{ env.DOCKER_REPO }}:${{ steps.meta.outputs.version }}" >> "$GITHUB_OUTPUT" + - name: Docker - Inspect image - run: | - docker buildx imagetools inspect ${{ env.DOCKER_REPO }}:${{ steps.meta.outputs.version }} + run: docker buildx imagetools inspect ${{ steps.image_tag.outputs.codex_image }} # Compute Tests inputs