diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml new file mode 100644 index 0000000..4993d52 --- /dev/null +++ b/.github/workflows/build-image.yml @@ -0,0 +1,160 @@ +name: Build Container Image & Test + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +on: + push: + # Publish semver tags as releases. + tags: ["v*.*.*"] + paths-ignore: + - "README.md" + branches: [ "**" ] + pull_request: + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: ${{ github.repository }} +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + Build_Container: + name: Build Docker Container + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Set up BuildKit Docker container builder to be able to build + # multi-platform images and export cache + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }}s + if: github.event_name != 'pull_request' + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build Docker image + id: buildandpush + uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 + with: + context: . + push: false + load: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Add Environment Variables + id: env + run: | + echo "GH_PULLREQ_NUM=${{ github.event.number }}" >> $GITHUB_ENV + echo "GH_EEVENT_NAME=${{ github.event_name }}" >> $GITHUB_ENV + echo "GH_RREPOSITORY=${{ github.repository }}" >> $GITHUB_ENV + - name: Create Tar Image For Upload + id: tar + run: | + docker images + tag=$(echo "${{ steps.meta.outputs.tags }}" | tr '\n' ' ') + echo $tag + docker save -o image.tar $tag + + - name: Upload Artifact + id: upload + uses: actions/upload-artifact@v4 + with: + name: image.tar + path: image.tar + retention-days: 1 # One Day (The Minimum) + outputs: + prnum: ${{ github.event.number }} + url: ${{ steps.upload.outputs.artifact-url }} + tag: ${{ env.IMAGE_NAME }} + artifact_id: ${{ steps.upload.outputs.artifact-id }} + + Test_Action: + name: Test Docker Container/GitHub Action + needs: Build_Container + uses: ./.github/workflows/test.yml + strategy: + fail-fast: true + matrix: + write_job_summary: [true, false] + repository: ["ZestCommunity/ZestCode"] + ref: ["main"] + caller_token: ["${{ github.token }}"] + with: + write_job_summary: ${{ matrix.write_job_summary }} + repository: ${{ matrix.repository }} + ref: ${{ matrix.ref }} + caller_token: ${{ matrix.caller_token }} + + Upload_Image: + name: Upload Docker Image to ghcr.io Registry + permissions: + contents: read + packages: write + needs: + [ + Build_Container, + Test_Action + ] + runs-on: ubuntu-latest + if: ${{ github.event_name != 'pull_request' }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + - name: Log into registry ${{ env.REGISTRY }} + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Download Image + uses: actions/download-artifact@v4 + with: + name: image.tar + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Load Image + id: load + run: | + echo "tag=$(docker load -i ./image.tar | grep -oP 'Loaded image: \K.*' | tr '\n' ' ')" > $GITHUB_OUTPUT + - name: Push the image + if: ${{ github.event_name != 'pull_request' }} + run: | + for tag in $(echo "${{ steps.load.outputs.tag }}" | tr ' ' '\n'); do + echo "$tag" + docker push "$tag" + done \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..331feef --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,60 @@ +name: Tests +on: + workflow_call: + inputs: + repository: + description: The github repository to checkout + required: true + type: string + ref: + description: The ref of the github repository to checkout + required: true + type: string + caller_token: + description: "The token of the caller" + required: true + type: string + write_job_summary: + description: Whether to write the artifact URL to the job summary + default: true + type: boolean + +jobs: + test: + name: "Testing Container: Write Job Summary?${{ inputs.write_job_summary }}" + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + repository: ${{ inputs.repository }} + ref: ${{ inputs.ref }} + + - name: Checkout + uses: actions/checkout@v4 + with: + path: ./action/ + - name: Download Artifact + uses: actions/download-artifact@v4 + with: + name: image.tar + github-token: ${{ inputs.caller_token }} + + - name: Load Image + id: load + run: | + echo "tag=$(docker load -i ./image.tar | grep -oP 'Loaded image: \K.*' | head -n 1)" > $GITHUB_OUTPUT + + - name: Edit Action.yml With Local Image + run: | + cat action/action.yml + sed -i "s|docker://ghcr.io/.*/build-action:[^\']*|docker://${{steps.load.outputs.tag}}|g" action/action.yml + sed -i "s|Dockerfile|docker://${{steps.load.outputs.tag}}|g" action/action.yml + cat action/action.yml + + - name: Test Action + id: test-action + uses: ./action/ + continue-on-error: False + with: + write_job_summary: ${{ inputs.write_job_summary }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..84a7730 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,52 @@ +# ------------ +# Build Stage: Get Dependencies +# ------------ +FROM alpine:latest AS get-dependencies +LABEL stage=builder + +LABEL org.opencontainers.image.description="A ZestCode Build Container" +LABEL org.opencontainers.image.source=https://github.com/ZestCommunity/build-action +LABEL org.opencontainers.image.licenses=MIT + +# Install Required Packages and ARM Toolchain +RUN apk add --no-cache bash +RUN mkdir "/arm-none-eabi-toolchain" && wget -O- "https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi.tar.xz" \ + | tar Jxf - -C "/arm-none-eabi-toolchain" --strip-components=1 +RUN <<-"EOF" bash + set -e + + toolchain="/arm-none-eabi-toolchain" + mkdir -p "$toolchain" + + rm -rf "$toolchain"/{share,include} + rm -rf "$toolchain"/lib/gcc/arm-none-eabi/13.3.1/arm + rm -f "$toolchain"/bin/arm-none-eabi-{gdb,gdb-py,cpp,gcc-13.3.1} + + find "$toolchain"/arm-none-eabi/lib/thumb -mindepth 1 -maxdepth 1 ! -name 'v7-a+simd' -exec rm -rf {} + + find "$toolchain"/lib/gcc/arm-none-eabi/13.3.1/thumb -mindepth 1 -maxdepth 1 ! -name 'v7-a+simd' -exec rm -rf {} + + find "$toolchain"/arm-none-eabi/include/c++/13.3.1/arm-none-eabi/thumb -mindepth 1 -maxdepth 1 ! -name 'v7-a+simd' -exec rm -rf {} + + + apk cache clean # Cleanup image +EOF +# ------------ +# Runner Stage +# ------------ +FROM alpine:latest AS runner +LABEL stage=runner +LABEL org.opencontainers.image.description="A ZestCode Build Container" +LABEL org.opencontainers.image.source=https://github.com/ZestCommunity/build-action +LABEL org.opencontainers.image.licenses=MIT +# Copy dependencies from get-dependencies stage +COPY --from=get-dependencies /arm-none-eabi-toolchain /arm-none-eabi-toolchain +RUN apk add --no-cache gcompat libc6-compat libstdc++ git gawk python3 pipx unzip bash && pipx install meson ninja && apk cache clean + +# Set Environment Variables +ENV PATH="/arm-none-eabi-toolchain/bin:/root/.local/bin:${PATH}" + +ENV PYTHONUNBUFFERED=1 + +COPY build-tools/build.sh /build.sh +RUN chmod +x /build.sh +COPY LICENSE /LICENSE + +ENTRYPOINT ["/build.sh"] diff --git a/README.md b/README.md index 9dcc4f6..be99844 100644 --- a/README.md +++ b/README.md @@ -1 +1,35 @@ -# build-action \ No newline at end of file +# ZestCode Build Action +An automated build action for ZestCode projects. + +## Usage: +### Example: +```yml +name: ZestCode Build Action Example + +on: + push: + branches: "**" + pull_request: + branches: "**" + + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run Build Action + id: test + uses: ZestCommunity/build-action@main +``` +### Inputs: +> `write_job_summary` +> - **Whether to output to GitHub's Job Summary** +> - Required: False +> - Default: True + +## Notes +This container was based on LemLib/pros-build. \ No newline at end of file diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..3c3464c --- /dev/null +++ b/action.yml @@ -0,0 +1,20 @@ +name: 'Zest Build Action' +description: 'Automatically build ZestCode templates' +branding: + color: 'blue' + icon: 'box' + colorized: true + +inputs: + write_job_summary: + required: false + default: true + description: Whether to create a GitHub job summary + +outputs: + name: + description: 'The recommended name for the artifact.' + +runs: + using: 'Docker' + image: 'Dockerfile' \ No newline at end of file diff --git a/build-tools/build.sh b/build-tools/build.sh new file mode 100644 index 0000000..32be56f --- /dev/null +++ b/build-tools/build.sh @@ -0,0 +1,118 @@ +#!/bin/bash +# git clone https://github.com/ZestCommunity/ZestCode.git && cd ZestCode && git switch build/meson-init && sed -i 's/-mfloat-abi=hard/-mfloat-abi=softfp/g' scripts/v5.ini && meson setup --cross-file scripts/v5.ini builddir && meson compile -C builddir + +disable-errors() { + set +e +} + +enable-errors() { + set -e +} + +# start time in seconds +script_start_time=$(date +%s) +build_start_time=-1 + +# ------------ +# ECHO LICENSE +# ------------ +disable-errors +echo "::group::License" +cat /LICENSE +echo "::endgroup::" + + +git config --global --add safe.directory /github/workspace + +# ------------ +# BUILD AND COMPILE PROJECT +# ------------\ +COMPILE_STD_OUTPUT=$(mktemp) +echo "::group::Build Project" +meson setup --cross-file scripts/v5.ini build | tee $COMPILE_STD_OUTPUT +echo "PIPESTATUS: ${PIPESTATUS[@]}" +meson_exit_code=${PIPESTATUS[0]} +echo "Meson setup exit code: $meson_exit_code" +if [ $meson_exit_code -ne 0 ]; then + echo "Meson setup failed. Please check the logs for more information." + if [ "$INPUT_WRITE_JOB_SUMMARY" == "true" ]; then + echo "# 🛑 Meson Setup Failed " > $GITHUB_STEP_SUMMARY + echo "Meson setup failed. Please check the logs for more information. " >> $GITHUB_STEP_SUMMARY + echo "***" >> $GITHUB_STEP_SUMMARY + echo "
Click to expand " >> $GITHUB_STEP_SUMMARY + echo " " >> $GITHUB_STEP_SUMMARY + echo "\`\`\` " >> $GITHUB_STEP_SUMMARY + echo " " >> $GITHUB_STEP_SUMMARY + cat $COMPILE_STD_OUTPUT >> $GITHUB_STEP_SUMMARY + echo " " >> $GITHUB_STEP_SUMMARY + echo "\`\`\` " >> $GITHUB_STEP_SUMMARY + echo " " >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + fi + echo "::endgroup::" + exit 1 +fi +echo "::endgroup::" + + + +echo "::group::Compile Project" + +STD_OUTPUT=$(mktemp) + +disable-errors +# time this command +start_time=$(date +%s) +meson compile -C build | tee $STD_OUTPUT +meson_exit_code=${PIPESTATUS[0]} +end_time=$(date +%s) +echo "Meson compile exit code: $meson_exit_code" +elapsed_time=$((end_time - start_time)) +echo "Meson compile took $elapsed_time seconds" +STD_EDITED_OUTPUT=$(mktemp) +# * Remove ANSI color codes from the output +# * https://stackoverflow.com/a/18000433 +sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2};?)?)?[mGK]//g" $STD_OUTPUT >$STD_EDITED_OUTPUT + +if [ $meson_exit_code -ne 0 ]; then + if [ "$INPUT_WRITE_JOB_SUMMARY" == "true" ]; then + echo "Meson compile failed. Please check the logs for more information." + echo "# 🛑 Meson Compile Failed " > $GITHUB_STEP_SUMMARY + echo "Meson compile failed in $elapsed_time seconds. Please check the logs for more information. " >> $GITHUB_STEP_SUMMARY + echo "***" >> $GITHUB_STEP_SUMMARY + echo "
Click to expand " >> $GITHUB_STEP_SUMMARY + echo " " >> $GITHUB_STEP_SUMMARY + echo "\`\`\`\n " >> $GITHUB_STEP_SUMMARY + echo " " >> $GITHUB_STEP_SUMMARY + cat $STD_EDITED_OUTPUT >> $GITHUB_STEP_SUMMARY + echo " " >> $GITHUB_STEP_SUMMARY + echo "\`\`\`\n " >> $GITHUB_STEP_SUMMARY + echo " " >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + fi + exit 1 +fi +echo "::endgroup::" + +# ------------ +# # BUILD SUCCESS +# ! FINAL SUMMARY +# ------------ +echo "The build was successful" +echo "The build took $elapsed_time seconds" + +# job summary +if [ "$INPUT_WRITE_JOB_SUMMARY" == "true" ]; then + echo "# ✅ Build Successful " > $GITHUB_STEP_SUMMARY + echo "The build was successful and took $elapsed_time seconds. " >> $GITHUB_STEP_SUMMARY + echo "***" >> $GITHUB_STEP_SUMMARY + echo "
Click to expand " >> $GITHUB_STEP_SUMMARY + echo " " >> $GITHUB_STEP_SUMMARY + echo "\`\`\` " >> $GITHUB_STEP_SUMMARY + echo " " >> $GITHUB_STEP_SUMMARY + cat $STD_EDITED_OUTPUT >> $GITHUB_STEP_SUMMARY + echo " " >> $GITHUB_STEP_SUMMARY + echo "\`\`\` " >> $GITHUB_STEP_SUMMARY + echo " " >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY +fi \ No newline at end of file