diff --git a/.github/workflows/PR-build-image-rust.yml b/.github/workflows/PR-build-image-rust.yml new file mode 100644 index 0000000..e2a1a7e --- /dev/null +++ b/.github/workflows/PR-build-image-rust.yml @@ -0,0 +1,30 @@ +env: + IMAGE_NAME: devcontainer-smart-contracts-rust + REGISTRY_HOSTNAME: multiversx + +name: Build Docker image - rust (PR) + +on: + pull_request: + +jobs: + build-docker-image: + strategy: + matrix: + runner: [ubuntu-latest, ubuntu-24.04-arm] + runs-on: ${{ matrix.runner }} + + steps: + - name: Check out the repo + uses: actions/checkout@v5 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build image + uses: docker/build-push-action@v6 + with: + context: ./resources/smart-contracts-rust + push: false + file: ./resources/smart-contracts-rust/Dockerfile + tags: ${{ env.REGISTRY_HOSTNAME }}/${{ env.IMAGE_NAME }}:pr-test diff --git a/.github/workflows/publish-image-rust.yml b/.github/workflows/publish-image-rust.yml deleted file mode 100644 index 865cac7..0000000 --- a/.github/workflows/publish-image-rust.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Publish Docker image (rust) - -on: - workflow_dispatch: - inputs: - latest: - description: "Push with tag 'latest'" - required: false - type: boolean - -jobs: - push_to_registry: - name: Push Docker image to Docker Hub - runs-on: ubuntu-latest - steps: - - name: Check out the repo - uses: actions/checkout@v5 - - - name: Set environment variables - run: | - VERSION=v$(jq -r '.["version"]' ./src/smart-contracts-rust/devcontainer-template.json) - echo "VERSION=$VERSION" >> $GITHUB_ENV - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: ./resources/smart-contracts-rust - push: true - platforms: linux/amd64 - file: ./resources/smart-contracts-rust/Dockerfile - tags: multiversx/devcontainer-smart-contracts-rust:${{ env.VERSION }} - - - name: Push to "latest" - uses: docker/build-push-action@v6 - with: - context: ./resources/smart-contracts-rust - push: ${{ inputs.latest }} - platforms: linux/amd64 - file: ./resources/smart-contracts-rust/Dockerfile - tags: multiversx/devcontainer-smart-contracts-rust:latest diff --git a/.github/workflows/release-publish-image-rust.yml b/.github/workflows/release-publish-image-rust.yml new file mode 100644 index 0000000..11fe7a8 --- /dev/null +++ b/.github/workflows/release-publish-image-rust.yml @@ -0,0 +1,119 @@ +env: + IMAGE_NAME: devcontainer-smart-contracts-rust + REGISTRY_HOSTNAME: multiversx + +name: Publish Docker image - rust (release) + +on: + release: + types: [published] + workflow_dispatch: + inputs: + version: + description: 'Simulate release version (e.g., v0.4.2)' + required: false + default: '' + type: string + +jobs: + prepare: + runs-on: ubuntu-latest + outputs: + tags: ${{ steps.meta.outputs.tags }} + tags-json: ${{ steps.meta.outputs.json }} + labels: ${{ steps.meta.outputs.labels }} + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Compute version from template (release) + id: version + if: github.event_name == 'release' + run: | + VERSION=v$(jq -r '.["version"]' ./src/smart-contracts-rust/devcontainer-template.json) + echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Extract metadata + id: meta + env: + VERSION: ${{ steps.version.outputs.VERSION }} + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY_HOSTNAME }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest + ${{ (github.event_name == 'release' && format('type=raw,value={0}', env.VERSION)) || (github.event.inputs.version != '' && format('type=raw,value={0}', github.event.inputs.version)) || '' }} + + build: + strategy: + matrix: + arch: + - { runner: ubuntu-latest, platform: linux/amd64, platform_tag: linux_amd64 } + - { runner: ubuntu-24.04-arm, platform: linux/arm64, platform_tag: linux_arm64 } + runs-on: ${{ matrix.arch.runner }} + needs: prepare + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v6 + with: + context: ./resources/smart-contracts-rust + file: ./resources/smart-contracts-rust/Dockerfile + platforms: ${{ matrix.arch.platform }} + labels: ${{ needs.prepare.outputs.labels }} + outputs: type=image,name=${{ env.REGISTRY_HOSTNAME }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + run: | + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ matrix.arch.platform_tag }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + manifest: + runs-on: ubuntu-latest + needs: [prepare, build] + + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: /tmp/digests + pattern: digests-* + merge-multiple: true + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Create and push manifest + working-directory: /tmp/digests + env: + TAGS_JSON: ${{ needs.prepare.outputs.tags-json }} + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$TAGS_JSON") \ + $(printf '${{ env.REGISTRY_HOSTNAME }}/${{ env.IMAGE_NAME }}@sha256:%s ' *) diff --git a/resources/smart-contracts-rust/Dockerfile b/resources/smart-contracts-rust/Dockerfile index 6b3ae8d..bb6f6f0 100644 --- a/resources/smart-contracts-rust/Dockerfile +++ b/resources/smart-contracts-rust/Dockerfile @@ -1,64 +1,59 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 -ARG USERNAME=developer -ARG USER_UID=1000 -ARG USER_GID=$USER_UID +ARG USERNAME=ubuntu +ARG VERSION_MXPY="11.3.1" +ARG VERSION_RUST="1.90.0" +ARG VERSION_SC_META="0.64.2" -ARG VERSION_MXPY="v11.1.1" -ARG VERSION_RUST="1.86.0" -ARG VERSION_SC_META="0.62.0" +ENV DEBIAN_FRONTEND=noninteractive -# Create the user -RUN groupadd --gid $USER_GID $USERNAME \ - && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \ - # - # [Optional] Add sudo support. Omit if you don't need to install software after connecting. - && apt-get update \ - && apt-get install -y sudo \ - && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ - && chmod 0440 /etc/sudoers.d/$USERNAME - -# Install some dependencies as root +# Base system dependencies RUN apt-get update && apt-get install -y \ - wget \ - build-essential \ - python3.10 python3-pip python3.10-venv \ + sudo \ + curl \ git \ + build-essential \ pkg-config \ - libssl-dev && \ - rm -rf /var/lib/apt/lists/* - -# Switch to regular user -USER $USERNAME + libssl-dev \ + python3 \ + python3-venv \ + python3-pip \ + pipx \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# Give sudo access to the user without password +RUN echo ${USERNAME} ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/${USERNAME} \ + && chmod 0440 /etc/sudoers.d/${USERNAME} + +USER ${USERNAME} WORKDIR /home/${USERNAME} +# Environment variables ENV MULTIVERSX="/home/${USERNAME}/multiversx-sdk" ENV PATH="/home/${USERNAME}/.local/bin:${PATH}" ENV PATH="/home/${USERNAME}/.cargo/bin:${PATH}" -# Install pipx -RUN python3 -m pip install --no-cache-dir --user pipx - -# Install mxpy +# Install mxpy via pipx RUN pipx install multiversx-sdk-cli==${VERSION_MXPY} -# Install rust -RUN wget -O rustup.sh https://sh.rustup.rs && \ - chmod +x rustup.sh && \ - CARGO_HOME=/home/${USERNAME}/.cargo RUSTUP_HOME=/home/${USERNAME}/.rustup ./rustup.sh --verbose --default-toolchain ${VERSION_RUST} --profile minimal -y && \ - rm rustup.sh +# Install Rust +RUN curl https://sh.rustup.rs -sSf | \ + sh -s -- -y --profile minimal --default-toolchain ${VERSION_RUST} # Install sc-meta tools -RUN CARGO_HOME=/home/${USERNAME}/.cargo RUSTUP_HOME=/home/${USERNAME}/.rustup PATH="/home/${USERNAME}/.cargo/bin:${PATH}" \ - cargo install multiversx-sc-meta --version ${VERSION_SC_META} --locked - -RUN CARGO_HOME=/home/${USERNAME}/.cargo RUSTUP_HOME=/home/${USERNAME}/.rustup PATH="/home/${USERNAME}/.cargo/bin:${PATH}" \ - sc-meta install all +RUN cargo install multiversx-sc-meta --version ${VERSION_SC_META} --locked \ + && sc-meta install all # Install test wallets -RUN mxpy deps install testwallets && rm ${MULTIVERSX}/*.tar.gz +RUN mxpy deps install testwallets + +# Rust components + cleanup +RUN rustup component add rustfmt \ + && rm -rf /home/${USERNAME}/.cargo/registry \ + && rm -rf /home/${USERNAME}/.cargo/git -RUN rustup component add rustfmt && rm -rf /home/${USERNAME}/.cargo/registry/* && rm -rf /home/${USERNAME}/.cargo/git/* +# Devcontainer resources +COPY post_create_command.py ${MULTIVERSX}/devcontainer-resources/ -# This command will be executed once the devcontainer is created -COPY "post_create_command.py" "${MULTIVERSX}/devcontainer-resources/" +CMD ["bash"] diff --git a/src/smart-contracts-rust/devcontainer-template.json b/src/smart-contracts-rust/devcontainer-template.json index e10a53e..524d77b 100644 --- a/src/smart-contracts-rust/devcontainer-template.json +++ b/src/smart-contracts-rust/devcontainer-template.json @@ -1,6 +1,6 @@ { "id": "smart-contracts-rust", - "version": "0.4.0", + "version": "0.5.0", "name": "MultiversX: Smart Contracts Development (Rust)", "description": "Develop smart contracts for MultiversX. Includes Rust, mxpy, VSCode extensions etc.", "documentationURL": "https://github.com/multiversx/mx-template-devcontainers/blob/main/src/smart-contracts-rust",