diff --git a/.github/workflows/build-and-upload-branch.yaml b/.github/workflows/build-and-upload-branch.yaml new file mode 100644 index 00000000..1021e9c3 --- /dev/null +++ b/.github/workflows/build-and-upload-branch.yaml @@ -0,0 +1,24 @@ +name: Build Dashboard (Branch) + +on: + push: + branches: + - main + - 'release-harvester-v*' + - '*-dev' + +jobs: + build-validation: + name: Build Test + uses: ./.github/workflows/test.yaml + build: + name: Build and Upload Package + uses: ./.github/workflows/build-and-upload.yaml + needs: + - build-validation + permissions: + contents: read + packages: write + id-token: write + with: + CI_BRANCH: ${{github.ref_name}} diff --git a/.github/workflows/build-and-upload.yaml b/.github/workflows/build-and-upload.yaml new file mode 100644 index 00000000..b8428c31 --- /dev/null +++ b/.github/workflows/build-and-upload.yaml @@ -0,0 +1,205 @@ +name: Build Harvester Dashboard and Upload + +on: + workflow_call: + inputs: + CI_BRANCH: + required: false + type: string + CI_BUILD_TAG: + required: false + type: string + +env: + GOOGLE_AUTH: '' + DOCKER_USERNAME: '' + DOCKER_PASSWORD: '' + CI_BUILD_TAG: ${{inputs.CI_BUILD_TAG}} + CI_BRANCH: ${{inputs.CI_BRANCH}} + GIT_REPO: ${{github.repository}} + GIT_COMMIT: ${{github.sha}} + REPO: ${{github.event.repository.name || ''}} + +jobs: + # build-and-upload-docker-image: + # name: Build & Upload Docker + # runs-on: ubuntu-latest + # permissions: + # contents: read + # packages: write + # id-token: write + # steps: + # - uses: actions/checkout@v4 + # with: + # fetch-depth: 1 + + # - id: upload-gate + # name: Upload Gate + # run: ./scripts/build-upload-gate + + # - id: read-docker-secrets + # name: Read Docker Secrets + # uses: rancher-eio/read-vault-secrets@main + # with: + # secrets: | + # secret/data/github/repo/${{ github.repository }}/dockerhub/harvester/credentials username | DOCKER_USERNAME ; + # secret/data/github/repo/${{ github.repository }}/dockerhub/harvester/credentials password | DOCKER_PASSWORD ; + + # - id: login-docker + # name: Docker Log in + # uses: docker/login-action@v3 + # with: + # username: ${{ env.DOCKER_USERNAME }} + # password: ${{ env.DOCKER_PASSWORD }} + + # - id: push-docker + # name: Docker Build + # uses: docker/build-push-action@v5 + # with: + # provenance: false + # context: . + # push: true + # tags: rancher/harvester-ui:${{ github.ref_name }} + + build-and-upload-hosted: + name: Build & Upload Hosted + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 1 + + # Note - Cannot use the setup action here as it uses a different yarn install arg + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'yarn' + + # Build a directory containing the dashboard that can be used with ui-dashboard-index + - id: build-hosted + name: Build Hosted + run: ./scripts/build-hosted + + - id: upload-gate + name: Upload Gate (superseded by a newer build?) + run: ./scripts/build-upload-gate + + - name: Get gcs auth + uses: rancher-eio/read-vault-secrets@main + with: + secrets: | + secret/data/github/repo/${{ github.repository }}/googleauthkey/harvester/credentials credential | GOOGLE_AUTH ; + + - name: Apply gcs auth + # https://github.com/google-github-actions/auth + uses: 'google-github-actions/auth@v2' + with: + credentials_json: "${{ env.GOOGLE_AUTH }}" + + - name: Upload build + uses: 'google-github-actions/upload-cloud-storage@v2' + # https://github.com/google-github-actions/upload-cloud-storage + with: + path: ${{steps.build-hosted.outputs.BUILD_HOSTED_DIR}} + destination: releases.rancher.com/harvester-ui/dashboard/${{ steps.build-hosted.outputs.BUILD_HOSTED_LOCATION }} + parent: false + headers: |- + cache-control: no-cache,must-revalidate + process_gcloudignore: false + + build-and-upload-embedded: + name: Build & Upload Embedded + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 1 + + # Note - Cannot use the setup action here as it uses a different yarn install arg + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'yarn' + + # Build a tar that will be picked up by rancher builds and embedded into it + - id: build-embedded + name: Build Embedded + run: ./scripts/build-embedded + env: + DISABLED_EMBED_PKG: https://releases.rancher.com/harvester-ui/plugin/harvester-1.0.3.tar.gz + + - name: Get gcs auth + uses: rancher-eio/read-vault-secrets@main + with: + secrets: | + secret/data/github/repo/${{ github.repository }}/googleauthkey/harvester/credentials credential | GOOGLE_AUTH ; + + - name: Apply gcs auth + # https://github.com/google-github-actions/auth + uses: 'google-github-actions/auth@v2' + with: + credentials_json: "${{ env.GOOGLE_AUTH }}" + + - name: Upload tar + uses: 'google-github-actions/upload-cloud-storage@v2' + with: + path: ${{steps.build-embedded.outputs.BUILD_EMBEDED_TGZ}} + destination: releases.rancher.com/harvester-ui/${{ env.REPO }} + parent: false + headers: |- + cache-control: no-cache,must-revalidate + process_gcloudignore: false + + build-and-upload-harvester-plugin: + name: Build & Upload Plugin + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 1 + + # Note - Cannot use the setup action here as it uses a different yarn install arg + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'yarn' + + - id: ci-build-pkg + name: Build pkg + run: ./shell/scripts/ci-build-pkg.sh harvester + + - id: upload-gate + name: Upload Gate + run: ./scripts/build-upload-gate + + - name: Get gcs auth + uses: rancher-eio/read-vault-secrets@main + with: + secrets: | + secret/data/github/repo/${{ github.repository }}/googleauthkey/harvester/credentials credential | GOOGLE_AUTH ; + + - name: Apply gcs auth + # https://github.com/google-github-actions/auth + uses: 'google-github-actions/auth@v2' + with: + credentials_json: "${{ env.GOOGLE_AUTH }}" + + - name: Upload plugin tar + uses: 'google-github-actions/upload-cloud-storage@v2' + with: + path: dist-pkg/${{steps.ci-build-pkg.outputs.PKG_TARBALL}} + destination: releases.rancher.com/harvester-ui/plugin + parent: false + headers: |- + cache-control: no-cache,must-revalidate + process_gcloudignore: false + + - name: Upload plugin directory + uses: 'google-github-actions/upload-cloud-storage@v2' + with: + path: dist-pkg/${{steps.ci-build-pkg.outputs.PKG_NAME}} + destination: releases.rancher.com/harvester-ui/plugin/${{steps.ci-build-pkg.outputs.PKG_NAME}} + parent: false + headers: |- + cache-control: no-cache,must-revalidate + process_gcloudignore: false diff --git a/scripts/build-embedded b/scripts/build-embedded new file mode 100755 index 00000000..4cd9e8da --- /dev/null +++ b/scripts/build-embedded @@ -0,0 +1,59 @@ +#!/bin/bash +set -e +set -x + +BUILD_DEBUG="${BUILD_DEBUG:-}" +if [[ -n "${BUILD_DEBUG}" ]]; then + set -x + env +fi + +cd $(dirname $0)/.. + +echo "Bootstrapping..." +yarn --pure-lockfile install + +source scripts/version +echo "BRANCH: ${COMMIT_BRANCH:-}" +echo "TAG: ${GIT_TAG:-}" +DIR=${GIT_TAG:-$COMMIT_BRANCH} + +OUTPUT_DIR=dist/${DIR}-embedded + +echo "Building..." +COMMIT=${COMMIT} VERSION=${VERSION} OUTPUT_DIR=$OUTPUT_DIR ROUTER_BASE='/dashboard/' RESOURCE_BASE='/dashboard/' RANCHER_ENV=harvester yarn run build + +if [ -v EMBED_PKG ]; then + echo "Build and embed plugin from: $EMBED_PKG" + PKG_FILE_NAME=${EMBED_PKG##*/} + echo PKG_FILE_NAME: $PKG_FILE_NAME + + PKG_NAME="${PKG_FILE_NAME/.tar.gz/""}" + echo "Plugin name: '$PKG_NAME'" + + # Fetch file, unpack and move to dist + curl $EMBED_PKG --output $PKG_FILE_NAME + OUTPUT_DIR_PKG=$OUTPUT_DIR/$PKG_NAME + mkdir -p $OUTPUT_DIR_PKG + tar xvfz $PKG_FILE_NAME -C $OUTPUT_DIR/$PKG_NAME + echo "Plugin contents that will be served from $PKG_NAME" + ls -alR $OUTPUT_DIR/$PKG_NAME +fi + +echo "Destroying..." +find $OUTPUT_DIR -name "index.html" -mindepth 2 -exec rm {} \; +find $OUTPUT_DIR -type d -empty -depth -exec rmdir {} \; + +TARBALL=${DIR}.tar.gz +TARBALL_FILE_PATH=dist/${TARBALL} + +ENV_OUTPUT="${GITHUB_OUTPUT:-"temp-env"}" +echo "BUILD_EMBEDED_TGZ=${TARBALL_FILE_PATH}" >> "$ENV_OUTPUT" + +echo "Compressing to ${TARBALL}..." +tar -czf ${TARBALL_FILE_PATH} $OUTPUT_DIR/ + +echo "Cleaning up..." +rm -r $OUTPUT_DIR + +ls -alR dist/ \ No newline at end of file diff --git a/scripts/build-hosted b/scripts/build-hosted new file mode 100755 index 00000000..990e8ce3 --- /dev/null +++ b/scripts/build-hosted @@ -0,0 +1,34 @@ +#!/bin/bash +set -e + +BUILD_DEBUG="${BUILD_DEBUG:-}" +if [[ -n "${BUILD_DEBUG}" ]]; then + set -x + env +fi + +cd $(dirname $0)/.. + +echo "Bootstrapping..." +yarn --pure-lockfile install + +source scripts/version +echo "BRANCH: ${COMMIT_BRANCH:-}" +echo "TAG: ${GIT_TAG:-}" +DIR=${GIT_TAG:-$COMMIT_BRANCH} + +if [[ "${DIR}" == "master" ]]; then + DIR="latest" +fi + +BASE=${BASE:-https://releases.rancher.com/harvester-ui/dashboard/${DIR}} + +echo "Building for ${BASE}..." + +OUTPUT_DIR=dist/${DIR} + +ENV_OUTPUT="${GITHUB_OUTPUT:-"temp-env"}" +echo "BUILD_HOSTED_DIR=${OUTPUT_DIR}" >> "$ENV_OUTPUT" +echo "BUILD_HOSTED_LOCATION=${DIR}" >> "$ENV_OUTPUT" + +COMMIT=${COMMIT} VERSION=${VERSION} OUTPUT_DIR=${OUTPUT_DIR} ROUTER_BASE="/dashboard/" RESOURCE_BASE="${BASE}" RANCHER_ENV=harvester yarn run build \ No newline at end of file diff --git a/scripts/build-upload-gate b/scripts/build-upload-gate new file mode 100755 index 00000000..13c494d1 --- /dev/null +++ b/scripts/build-upload-gate @@ -0,0 +1,63 @@ +#!/bin/bash +set -e + +cd $(dirname $0)/.. + +BUILD_DEBUG="${BUILD_DEBUG:-}" +if [[ -n "${BUILD_DEBUG}" ]]; then + set -x + env +fi + +if [[ -n "$CI_BUILD_TAG" ]]; then + echo "Build has been triggered by a tag. Skipping gate" + exit 0 +fi + +if [[ -z "$CI_BRANCH" ]]; then + echo "No branch detected. Skipping gate" + exit 0 +fi + +if [[ -z "$GIT_REPO" ]]; then + echo "No repository detected. Unable to gate" + exit 1 +fi + +if [[ -z "$GIT_COMMIT" ]]; then + echo "No commit detected. Unable to gate" + exit 1 +fi + +echo "Repo: $GIT_REPO" +echo "Branch: $CI_BRANCH" +echo "Build Commit: $GIT_COMMIT" + +tmp=$(mktemp -u) +STATUS_CODE=$(curl -w "%{http_code}" -s -o $tmp https://api.github.com/repos/$GIT_REPO/branches/$CI_BRANCH) + +if [ "$STATUS_CODE" == "403" ]; then + RATE_LIMIT_REMAINING=$(curl -s https://api.github.com/rate_limit | ./scripts/jq-nano - rate remaining) + echo "Remaining GITHUB requests available: $RATE_LIMIT_REMAINING" + RATE_LIMIT_REMAINING=${RATE_LIMIT_REMAINING:-0} + if ((RATE_LIMIT_REMAINING < 1)); then + echo "Github Rate Limit has been hit, skipping gate" + exit 0 + fi + # Fall through to the normal path like any other failed status code +fi + +LATEST_BRANCH_COMMIT=$(cat $tmp | ./scripts/jq-nano - commit sha) +rm $tmp +echo "Latest Branch Commit: $LATEST_BRANCH_COMMIT" +if [ -z "$LATEST_BRANCH_COMMIT" ]; then + echo "Unable to determine latest commit, failing gate" + exit 1 +fi + +if [ "$LATEST_BRANCH_COMMIT" == "$GIT_COMMIT" ]; then + echo "Build was created from latest commit, passed upload gate" +else + echo "Build was not created from latest commit, failing gate" + exit 1 +fi