diff --git a/.github/actions/auto-tag/action.yml b/.github/actions/auto-tag/action.yml deleted file mode 100644 index b64ff3f..0000000 --- a/.github/actions/auto-tag/action.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: "Cloud-Neutral Auto Tag" -description: "Generate Docker tags for main, release, PR and dev branches" -inputs: - image: - description: "Base image name (e.g. ghcr.io/.../image)" - required: true - -outputs: - tags: - description: "Generated Docker tags" - value: ${{ steps.meta.outputs.tags }} - labels: - description: "Generated Docker labels" - value: ${{ steps.meta.outputs.labels }} - -runs: - using: composite - steps: - - name: Generate metadata (auto tags) - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ inputs.image }} - - tags: | - # main → latest - type=raw,enable=${{ github.ref == 'refs/heads/main' }},value=latest - - # release tag(v1.2.3) - type=ref,event=tag - type=semver,pattern={{version}} - - # PR → pr-123 - type=raw,enable=${{ startsWith(github.ref, 'refs/pull/') }},value=pr-${{ github.event.pull_request.number }} - - # dev/feature branches → branch name - type=ref,event=branch diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml deleted file mode 100644 index 18045c0..0000000 --- a/.github/actions/build/action.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: Build -description: Build artifacts for each service and platform with optional container publishing. -inputs: - service: - description: Target service name - required: true - platform: - description: Target platform (e.g., linux/amd64) - required: true - environment: - description: Deployment environment (dev or prod) - required: true -runs: - using: composite - steps: - - name: Prepare matrix context - id: matrix - uses: ../matrix-support - with: - service: ${{ inputs.service }} - platform: ${{ inputs.platform }} - environment: ${{ inputs.environment }} - enable_docker: 'true' - - - name: Cache build artifacts - uses: actions/cache@v4 - with: - path: | - build/${{ inputs.service }} - dashboard/.next - key: build-${{ inputs.service }}-${{ inputs.platform }}-${{ hashFiles('**/go.sum', 'dashboard/yarn.lock') }}-${{ inputs.environment }} - restore-keys: | - build-${{ inputs.service }}-${{ inputs.platform }}- - build-${{ inputs.service }}- - - - name: Prepare Go toolchain - if: inputs.service != 'dashboard' - uses: actions/setup-go@v4 - with: - go-version: '1.22' - cache: true - - - name: Build Go binaries - if: inputs.service != 'dashboard' - shell: bash - run: | - set -euo pipefail - goos="${{ steps.matrix.outputs.goos }}" - goarch="${{ steps.matrix.outputs.goarch }}" - mkdir -p build/${{ inputs.service }}/"${goos}-${goarch}" - declare -a targets - if [[ "${{ inputs.service }}" == "rag-server" ]]; then - targets=("rag-server/cmd/xcontrol-server" "rag-server/cmd/rag-server-cli") - elif [[ "${{ inputs.service }}" == "account" ]]; then - targets=("account/cmd/accountsvc") - else - targets=("./...") - fi - for target in "${targets[@]}"; do - binary_name=$(basename "$target") - GOOS="$goos" GOARCH="$goarch" go build -o build/${{ inputs.service }}/"${goos}-${goarch}"/"${binary_name}" "$target" - done - - - name: Upload Go artifacts - if: inputs.service != 'dashboard' - uses: actions/upload-artifact@v4 - with: - name: ${{ inputs.service }}-${{ inputs.platform }}-${{ inputs.environment }} - path: build/${{ inputs.service }}/ - - - name: Install dashboard dependencies - if: inputs.service == 'dashboard' - working-directory: dashboard - shell: bash - run: yarn install --frozen-lockfile - - - name: Build dashboard - if: inputs.service == 'dashboard' - working-directory: dashboard - shell: bash - env: - NEXT_PUBLIC_ENV: ${{ inputs.environment }} - run: yarn build - - - name: Upload dashboard build output - if: inputs.service == 'dashboard' - uses: actions/upload-artifact@v4 - with: - name: dashboard-${{ inputs.platform }}-${{ inputs.environment }} - path: dashboard/.next diff --git a/.github/actions/code-quality/action.yml b/.github/actions/code-quality/action.yml deleted file mode 100644 index 4be4a89..0000000 --- a/.github/actions/code-quality/action.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Code Quality -description: Run linting and basic quality checks per service/platform/environment matrix entry. -inputs: - service: - description: Target service name - required: true - platform: - description: Target platform (e.g., linux/amd64) - required: true - environment: - description: Deployment environment (dev or prod) - required: true -runs: - using: composite - steps: - - name: Prepare matrix context - id: matrix - uses: ./.github/actions/matrix-support - with: - service: ${{ inputs.service }} - platform: ${{ inputs.platform }} - environment: ${{ inputs.environment }} - - - name: Install git-secrets - shell: bash - run: | - set -euo pipefail - git clone https://github.com/awslabs/git-secrets.git - sudo make install -C git-secrets - git secrets --install - git secrets --scan - - - name: Go vet - if: inputs.service != 'dashboard' - shell: bash - run: go vet ./... - - - name: Go unit tests (quality gate) - if: inputs.service != 'dashboard' - shell: bash - run: go test ./... - - - name: Install dashboard dependencies - if: inputs.service == 'dashboard' - working-directory: dashboard - shell: bash - run: yarn install --frozen-lockfile - - - name: Dashboard lint - if: inputs.service == 'dashboard' - working-directory: dashboard - shell: bash - run: yarn lint diff --git a/.github/actions/deploy/action.yml b/.github/actions/deploy/action.yml deleted file mode 100644 index dd6238a..0000000 --- a/.github/actions/deploy/action.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Deploy -description: Coordinate deployments per service/environment. -inputs: - service: - description: Target service name - required: true - platform: - description: Target platform (e.g., linux/amd64) - required: true - environment: - description: Deployment environment (dev or prod) - required: true -runs: - using: composite - steps: - - name: Prepare matrix context - id: matrix - uses: ./.github/actions/matrix-support - with: - service: ${{ inputs.service }} - platform: ${{ inputs.platform }} - environment: ${{ inputs.environment }} - - - name: Prepare rollout context - id: context - shell: bash - run: | - set -euo pipefail - echo "service=${{ inputs.service }}" >> "$GITHUB_OUTPUT" - echo "environment=${{ inputs.environment }}" >> "$GITHUB_OUTPUT" - echo "platform=${{ inputs.platform }}" >> "$GITHUB_OUTPUT" - echo "release_channel=${{ steps.matrix.outputs.is_prod == 'true' && 'prod' || 'dev' }}" >> "$GITHUB_OUTPUT" - - - name: Deploy placeholder - shell: bash - env: - TARGET_ENV: ${{ steps.context.outputs.environment }} - TARGET_SERVICE: ${{ steps.context.outputs.service }} - TARGET_PLATFORM: ${{ steps.context.outputs.platform }} - RELEASE_CHANNEL: ${{ steps.context.outputs.release_channel }} - run: | - echo "Deploying ${TARGET_SERVICE} (${TARGET_PLATFORM}) to ${TARGET_ENV} namespace via ${RELEASE_CHANNEL} rollout" - echo "Hook in Helm/kubectl/ArgoCD rollouts here" - - - name: Rollback plan - shell: bash - run: | - echo "Rollback can be re-run per matrix entry by dispatching with allow_deploy=true" diff --git a/.github/actions/docker-login/action.yml b/.github/actions/docker-login/action.yml deleted file mode 100644 index 4c148fe..0000000 --- a/.github/actions/docker-login/action.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Docker Login -description: Login to Docker registry using docker CLI - -inputs: - registry: - description: Docker registry URL - required: true - username: - description: Username for registry login - required: true - password: - description: Password or token for registry login - required: true - -runs: - using: composite - steps: - - shell: bash - run: | - echo "${{ inputs.password }}" | docker login -u "${{ inputs.username }}" --password-stdin ${{ inputs.registry }} diff --git a/.github/actions/docker-setup-buildx/action.yml b/.github/actions/docker-setup-buildx/action.yml deleted file mode 100644 index 70c4130..0000000 --- a/.github/actions/docker-setup-buildx/action.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: docker-setup-buildx -description: Wrapper around docker/setup-buildx-action pinned to a SHA inside cloud-neutral-toolkit repo. -runs: - using: composite - steps: - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f diff --git a/.github/actions/docker-setup-qemu/action.yml b/.github/actions/docker-setup-qemu/action.yml deleted file mode 100644 index 5c9a543..0000000 --- a/.github/actions/docker-setup-qemu/action.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: Docker Setup QEMU -description: Set up QEMU for multi-platform builds - -runs: - using: composite - steps: - - shell: bash - run: | - # Install QEMU emulation support - docker run --privileged --rm tonistiigi/binfmt --install all diff --git a/.github/actions/download-artifact/action.yml b/.github/actions/download-artifact/action.yml deleted file mode 100644 index 1dd42be..0000000 --- a/.github/actions/download-artifact/action.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Download Artifact -description: Download artifact files - -inputs: - name: - description: Artifact name - required: true - -runs: - using: composite - steps: - - shell: bash - run: | - # Restore artifact from artifacts directory - if [ -d "artifacts/${{ inputs.name }}" ]; then - cp -r artifacts/${{ inputs.name }}/* . || true - elif [ -f "${{ inputs.name }}" ]; then - echo "Artifact file found: ${{ inputs.name }}" - else - echo "Artifact not found: ${{ inputs.name }}" - exit 1 - fi diff --git a/.github/actions/matrix-support/action.yml b/.github/actions/matrix-support/action.yml deleted file mode 100644 index 765368c..0000000 --- a/.github/actions/matrix-support/action.yml +++ /dev/null @@ -1,106 +0,0 @@ -name: Matrix Support -description: Common setup for matrix-driven workflows with language and cache bootstrapping. -inputs: - service: - description: Target service name - required: true - platform: - description: Target platform (e.g., linux/amd64) - required: true - environment: - description: Deployment environment (dev or prod) - required: true - enable_docker: - description: Enable Docker buildx/QEMU setup - required: false - default: 'false' -outputs: - goos: - description: Derived GOOS from the platform input - value: ${{ steps.platforms.outputs.goos }} - goarch: - description: Derived GOARCH from the platform input - value: ${{ steps.platforms.outputs.goarch }} - is_prod: - description: Whether the environment is prod or the ref is a tag - value: ${{ steps.flags.outputs.is_prod }} - target_platforms: - description: Platform list for builds (single in dev, multi-arch in prod) - value: ${{ steps.flags.outputs.target_platforms }} -runs: - using: composite - steps: - - name: Checkout - - uses: actions/checkout@v4 - - - name: Derive platform matrix values - id: platforms - shell: bash - run: | - set -euo pipefail - platform="${{ inputs.platform }}" - goos="${platform%%/*}" - goarch="${platform##*/}" - echo "goos=${goos}" >> "$GITHUB_OUTPUT" - echo "goarch=${goarch}" >> "$GITHUB_OUTPUT" - - - name: Resolve environment flags - id: flags - shell: bash - run: | - set -euo pipefail - if [[ "${{ inputs.environment }}" == "prod" || "${GITHUB_REF_TYPE:-}" == "tag" ]]; then - echo "is_prod=true" >> "$GITHUB_OUTPUT" - echo "target_platforms=linux/amd64,linux/arm64" >> "$GITHUB_OUTPUT" - else - echo "is_prod=false" >> "$GITHUB_OUTPUT" - echo "target_platforms=${{ inputs.platform }}" >> "$GITHUB_OUTPUT" - fi - - - name: Set up Go - if: inputs.service != 'dashboard' - uses: actions/setup-go@v4 - with: - go-version: '1.22' - cache: true - - - name: Cache Go build data - if: inputs.service != 'dashboard' - uses: actions/cache@v4 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: go-${{ inputs.service }}-${{ inputs.platform }}-${{ hashFiles('**/go.sum') }} - restore-keys: | - go-${{ inputs.service }}-${{ inputs.platform }}- - go-${{ inputs.service }}- - - - name: Set up Node.js - if: inputs.service == 'dashboard' - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: yarn - cache-dependency-path: dashboard/yarn.lock - - - name: Cache dashboard artifacts - if: inputs.service == 'dashboard' - uses: actions/cache@v4 - with: - path: | - dashboard/.next/cache - ~/.cache/yarn - key: dashboard-${{ inputs.platform }}-${{ hashFiles('dashboard/yarn.lock') }} - restore-keys: | - dashboard-${{ inputs.platform }}- - dashboard- - - - name: Enable Docker build tooling - if: inputs.enable_docker == 'true' - uses: docker/setup-qemu-action@v3 - - - name: Set up buildx - if: inputs.enable_docker == 'true' - uses: docker/setup-buildx-action@v3 diff --git a/.github/actions/security/action.yml b/.github/actions/security/action.yml deleted file mode 100644 index 27bbb9f..0000000 --- a/.github/actions/security/action.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Security -description: Security scanning per service/platform/environment. -inputs: - service: - description: Target service name - required: true - platform: - description: Target platform (e.g., linux/amd64) - required: true - environment: - description: Deployment environment (dev or prod) - required: true -runs: - using: composite - steps: - - name: Prepare matrix context - id: matrix - uses: ./.github/actions/matrix-support - with: - service: ${{ inputs.service }} - platform: ${{ inputs.platform }} - environment: ${{ inputs.environment }} - - - name: Run golangci-lint - if: inputs.service != 'dashboard' - uses: golangci/golangci-lint-action@v6 - with: - version: latest - args: ./... - - - name: Install gosec - if: inputs.service != 'dashboard' - shell: bash - run: go install github.com/securego/gosec/v2/cmd/gosec@latest - - - name: Run gosec - if: inputs.service != 'dashboard' - shell: bash - run: gosec ./... - - - name: Trivy filesystem scan - if: inputs.service != 'dashboard' - uses: aquasecurity/trivy-action@0.24.0 - with: - scan-type: fs - scan-ref: . - severity: HIGH,CRITICAL - ignore-unfixed: true - format: table - exit-code: "0" - - - name: Install dashboard dependencies - if: inputs.service == 'dashboard' - working-directory: dashboard - shell: bash - run: yarn install --frozen-lockfile - - - name: Run ESLint - if: inputs.service == 'dashboard' - working-directory: dashboard - shell: bash - run: yarn lint - - - name: Semgrep security rules - if: inputs.service == 'dashboard' - uses: returntocorp/semgrep-action@v1 - with: - config: p/ci - paths: dashboard - - - name: npm audit (production) - if: inputs.service == 'dashboard' - working-directory: dashboard - shell: bash - run: npm audit --production - continue-on-error: true diff --git a/.github/actions/test/action.yml b/.github/actions/test/action.yml deleted file mode 100644 index bf8dba6..0000000 --- a/.github/actions/test/action.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Test -description: Run service-specific tests. -inputs: - service: - description: Target service name - required: true - platform: - description: Target platform (e.g., linux/amd64) - required: true - environment: - description: Deployment environment (dev or prod) - required: true -runs: - using: composite - steps: - - name: Prepare matrix context - id: matrix - uses: ./.github/actions/matrix-support - with: - service: ${{ inputs.service }} - platform: ${{ inputs.platform }} - environment: ${{ inputs.environment }} - - - name: Run Go integration tests - if: inputs.service != 'dashboard' - shell: bash - run: | - set -euo pipefail - go test ./... -run Integration -count=1 - - - name: Install dashboard dependencies - if: inputs.service == 'dashboard' - working-directory: dashboard - shell: bash - run: yarn install --frozen-lockfile - - - name: Run dashboard unit tests - if: inputs.service == 'dashboard' - working-directory: dashboard - shell: bash - env: - NODE_ENV: ${{ inputs.environment }} - run: yarn test:unit - - - name: Run dashboard e2e tests - if: inputs.service == 'dashboard' - working-directory: dashboard - shell: bash - env: - PORT: 3100 - NODE_ENV: ${{ inputs.environment }} - run: yarn test:e2e diff --git a/.github/actions/upload-artifact/action.yml b/.github/actions/upload-artifact/action.yml deleted file mode 100644 index e154299..0000000 --- a/.github/actions/upload-artifact/action.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Upload Artifact -description: Upload artifact files - -inputs: - name: - description: Artifact name - required: true - path: - description: File paths to upload - required: true - -runs: - using: composite - steps: - - shell: bash - run: | - # Create artifacts directory - mkdir -p artifacts - - # Copy files to artifacts directory - cp -r ${{ inputs.path }} artifacts/ - - # Save artifact metadata - echo "${{ inputs.name }}" > artifacts/metadata.txt - echo "${{ inputs.path }}" >> artifacts/metadata.txt - ls -la artifacts/ >> artifacts/metadata.txt - - # For now, just keep files in workspace - # In CI/CD system, this would be collected - echo "Artifact uploaded to artifacts/ directory" diff --git a/.github/scripts/build-images/copy-image-to-dockerhub.sh b/.github/scripts/build-images/copy-image-to-dockerhub.sh deleted file mode 100644 index 0b68054..0000000 --- a/.github/scripts/build-images/copy-image-to-dockerhub.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -: "${ORG:?ORG is required}" -: "${MANIFEST_DIGEST:?MANIFEST_DIGEST is required}" -: "${TARGET_NS:?TARGET_NS is required}" -: "${GHCR_USERNAME:?GHCR_USERNAME is required}" -: "${GHCR_TOKEN:?GHCR_TOKEN is required}" -: "${DOCKERHUB_USERNAME:?DOCKERHUB_USERNAME is required}" -: "${DOCKERHUB_TOKEN:?DOCKERHUB_TOKEN is required}" - -sudo apt-get update -y -sudo apt-get install -y skopeo - -src="docker://ghcr.io/${ORG}/dashboard@${MANIFEST_DIGEST}" -dst="docker://docker.io/${TARGET_NS}/dashboard:latest" - -skopeo login ghcr.io -u "${GHCR_USERNAME}" -p "${GHCR_TOKEN}" -skopeo login docker.io -u "${DOCKERHUB_USERNAME}" -p "${DOCKERHUB_TOKEN}" -skopeo copy --all "${src}" "${dst}" diff --git a/.github/scripts/build-images/create-ghcr-manifest.sh b/.github/scripts/build-images/create-ghcr-manifest.sh deleted file mode 100644 index db71e8d..0000000 --- a/.github/scripts/build-images/create-ghcr-manifest.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -: "${REGISTRY:?REGISTRY is required}" -: "${ORG:?ORG is required}" -: "${IMAGE_SHA:?IMAGE_SHA is required}" -: "${AMD_DIGEST:?AMD_DIGEST is required}" -: "${ARM_DIGEST:?ARM_DIGEST is required}" -: "${TAGS_CSV:?TAGS_CSV is required}" -: "${GITHUB_ENV:?GITHUB_ENV is required}" - -src_amd="${REGISTRY}/${ORG}/dashboard:build-${IMAGE_SHA}-linux-amd64@${AMD_DIGEST}" -src_arm="${REGISTRY}/${ORG}/dashboard:build-${IMAGE_SHA}-linux-arm64@${ARM_DIGEST}" - -first_tag="" -while IFS= read -r tag; do - [ -n "${tag}" ] || continue - if [ -z "${first_tag}" ]; then - first_tag="${tag}" - fi - docker buildx imagetools create -t "${tag}" "${src_amd}" "${src_arm}" -done < <(printf '%s' "${TAGS_CSV}" | tr ',' '\n') - -[ -n "${first_tag}" ] || { - echo "No tags were generated." >&2 - exit 1 -} - -manifest_digest="$(docker buildx imagetools inspect "${first_tag}" --format '{{.Digest}}')" -echo "MANIFEST_DIGEST=${manifest_digest}" >> "${GITHUB_ENV}" -echo "FINAL_TAG=${first_tag}" >> "${GITHUB_ENV}" diff --git a/.github/scripts/build-images/load-image-digest.sh b/.github/scripts/build-images/load-image-digest.sh deleted file mode 100644 index 723c1a0..0000000 --- a/.github/scripts/build-images/load-image-digest.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -: "${DIGEST_FILE:?DIGEST_FILE is required}" -: "${GITHUB_ENV:?GITHUB_ENV is required}" - -echo "IMAGE_DIGEST=$(tr -d '\n' < "${DIGEST_FILE}")" >> "${GITHUB_ENV}" diff --git a/.github/scripts/build-images/load-manifest-digests.sh b/.github/scripts/build-images/load-manifest-digests.sh deleted file mode 100644 index 7900364..0000000 --- a/.github/scripts/build-images/load-manifest-digests.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -: "${AMD_DIGEST_FILE:?AMD_DIGEST_FILE is required}" -: "${ARM_DIGEST_FILE:?ARM_DIGEST_FILE is required}" -: "${GITHUB_ENV:?GITHUB_ENV is required}" - -echo "AMD_DIGEST=$(tr -d '\n' < "${AMD_DIGEST_FILE}")" >> "${GITHUB_ENV}" -echo "ARM_DIGEST=$(tr -d '\n' < "${ARM_DIGEST_FILE}")" >> "${GITHUB_ENV}" diff --git a/.github/scripts/build-images/record-digest.sh b/.github/scripts/build-images/record-digest.sh deleted file mode 100644 index 7165c25..0000000 --- a/.github/scripts/build-images/record-digest.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -: "${IMAGE_DIGEST:?IMAGE_DIGEST is required}" -: "${OUTPUT_FILE:?OUTPUT_FILE is required}" - -printf '%s\n' "${IMAGE_DIGEST}" > "${OUTPUT_FILE}" diff --git a/.github/scripts/build-images/set-image-ref.sh b/.github/scripts/build-images/set-image-ref.sh deleted file mode 100644 index 023ac31..0000000 --- a/.github/scripts/build-images/set-image-ref.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -: "${REGISTRY:?REGISTRY is required}" -: "${ORG:?ORG is required}" -: "${SERVICE_NAME:?SERVICE_NAME is required}" -: "${IMAGE_DIGEST:?IMAGE_DIGEST is required}" -: "${IMAGE_SHA:?IMAGE_SHA is required}" -: "${IMAGE_ARTIFACT:?IMAGE_ARTIFACT is required}" -: "${GITHUB_ENV:?GITHUB_ENV is required}" - -echo "IMG=${REGISTRY}/${ORG}/${SERVICE_NAME}:build-${IMAGE_SHA}-${IMAGE_ARTIFACT}@${IMAGE_DIGEST}" >> "${GITHUB_ENV}" diff --git a/.github/scripts/build-images/validate-blog-content-mount.sh b/.github/scripts/build-images/validate-blog-content-mount.sh deleted file mode 100644 index 9a550d6..0000000 --- a/.github/scripts/build-images/validate-blog-content-mount.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -: "${IMAGE:?IMAGE is required}" -: "${KNOWLEDGE_CONTENT_DIR:?KNOWLEDGE_CONTENT_DIR is required}" - -docker pull "${IMAGE}" -docker run --rm \ - -v "${KNOWLEDGE_CONTENT_DIR}:/app/dashboard/src/content/blog:ro" \ - "${IMAGE}" \ - sh -c 'test -d /app/dashboard/src/content/blog' diff --git a/.github/scripts/check-image/check-images.sh b/.github/scripts/check-image/check-images.sh deleted file mode 100644 index 7bfd26c..0000000 --- a/.github/scripts/check-image/check-images.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -: "${TAG:?TAG is required}" -: "${IMAGE_LIST_FILE:?IMAGE_LIST_FILE is required}" - -while IFS= read -r image; do - [ -n "${image}" ] || continue - echo "Checking ${image}:${TAG}" - docker manifest inspect "${image}:${TAG}" > /dev/null - docker pull "${image}:${TAG}" > /dev/null -done < "${IMAGE_LIST_FILE}" diff --git a/.github/scripts/check-image/images.txt b/.github/scripts/check-image/images.txt deleted file mode 100644 index 9c9e525..0000000 --- a/.github/scripts/check-image/images.txt +++ /dev/null @@ -1,12 +0,0 @@ -ghcr.io/cloud-neutral-toolkit/openresty-geoip -ghcr.io/cloud-neutral-toolkit/postgres-runtime -ghcr.io/cloud-neutral-toolkit/account -ghcr.io/cloud-neutral-toolkit/dashboard -ghcr.io/cloud-neutral-toolkit/rag-server -ghcr.io/cloud-neutral-toolkit/xcontrol-init -docker.io/cloudneutral/openresty-geoip -docker.io/cloudneutral/postgres-runtime -docker.io/cloudneutral/account -docker.io/cloudneutral/dashboard -docker.io/cloudneutral/rag-server -docker.io/cloudneutral/xcontrol-init diff --git a/.github/scripts/cosign/sign.sh b/.github/scripts/cosign/sign.sh deleted file mode 100755 index ca4fec1..0000000 --- a/.github/scripts/cosign/sign.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -set -e - -REG="ghcr.io/cloud-neutral-toolkit" - -cosign sign --yes "$REG/node-builder@$NODE_BUILDER_DIGEST" -cosign sign --yes "$REG/node-runtime@$NODE_RUNTIME_DIGEST" -cosign sign --yes "$REG/openresty-geoip@$OPENRESTY_GEOIP_DIGEST" -cosign sign --yes "$REG/postgres-runtime@$POSTGRES_RUNTIME_DIGEST" diff --git a/.github/scripts/metadata/gen.py b/.github/scripts/metadata/gen.py deleted file mode 100755 index 9722d2d..0000000 --- a/.github/scripts/metadata/gen.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python3 -import json, sys - -if len(sys.argv) < 4: - print("Usage: gen.py ") - sys.exit(1) - -name = sys.argv[1] -digest = sys.argv[2] -raw_tags = sys.argv[3] -tags = raw_tags.splitlines() - -preferred = next((t for t in tags if t.endswith(":latest")), tags[0] if tags else "") - -metadata = { - "name": name, - "digest": digest, - "tags": tags, - "preferred_tag": preferred, - "image": f"ghcr.io/cloud-neutral-toolkit/{name}", - "image_with_digest": f"ghcr.io/cloud-neutral-toolkit/{name}@{digest}", -} - -outfile = f"image-metadata-{name}.json" -with open(outfile, "w", encoding="utf-8") as f: - json.dump(metadata, f, indent=2) - -print(f"[metadata] Wrote: {outfile}") diff --git a/.github/scripts/sbom/generate.sh b/.github/scripts/sbom/generate.sh deleted file mode 100755 index 1a7320b..0000000 --- a/.github/scripts/sbom/generate.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -set -e - -IMAGE="$1" -OUT="$2" - -anchore-cli sbom generate "$IMAGE" -o "$OUT" diff --git a/.github/scripts/utils/preferred-tag.sh b/.github/scripts/utils/preferred-tag.sh deleted file mode 100755 index a4f5246..0000000 --- a/.github/scripts/utils/preferred-tag.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -set -e - -tags="$1" -preferred="" - -while IFS= read -r line; do - [[ "$line" == *":latest" ]] && preferred="$line" && break -done <<< "$tags" - -if [[ -z "$preferred" ]]; then - preferred="$(echo "$tags" | head -n 1)" -fi - -echo "$preferred" diff --git a/.github/workflows/build-images.yml b/.github/workflows/build-images.yml deleted file mode 100644 index 65ef5c9..0000000 --- a/.github/workflows/build-images.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Build Multi-Arch Images - -env: - REGISTRY: ghcr.io - ORG: ${{ github.repository_owner }} - SKIP_SECURITY: ${{ inputs.skip_security || github.event.inputs.skip_security || 'false' }} - NODE_BUILDER_IMAGE: ${{ inputs.node_builder_image || github.event.inputs.node_builder_image || 'node:22-bookworm' }} - NODE_RUNTIME_IMAGE: ${{ inputs.node_runtime_image || github.event.inputs.node_runtime_image || 'node:22-slim' }} - PUSH_IMAGES: ${{ github.event_name == 'push' - || (github.event_name == 'workflow_call' && inputs.push_images) - || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true') }} - -jobs: - build: - runs-on: ubuntu-latest - - strategy: - matrix: - arch: - - { platform: linux/amd64, artifact: linux-amd64 } - - { platform: linux/arm64, artifact: linux-arm64 } - service: - - { name: dashboard, workdir: ., dockerfile: Dockerfile } - - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 - - - uses: ./.github/actions/docker-login - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Generate Auto Tags - id: meta - uses: ./.github/actions/auto-tag - with: - image: ${{ env.REGISTRY }}/${{ env.ORG }}/${{ matrix.service.name }} - - - uses: ./.github/actions/docker-setup-qemu - - uses: ./.github/actions/docker-setup-buildx - - - name: Clone knowledge content - run: git clone https://github.com/Cloud-Neutral-Workshop/knowledge.git knowledge - - - name: Build Service Image (per-arch) - id: build - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 - with: - context: ${{ matrix.service.workdir }} - file: ${{ matrix.service.dockerfile }} - platforms: ${{ matrix.arch.platform }} - push: ${{ env.PUSH_IMAGES }} - tags: | - ${{ env.REGISTRY }}/${{ env.ORG }}/${{ matrix.service.name }}:build-${{ github.sha }}-${{ matrix.arch.artifact }} - labels: ${{ steps.meta.outputs.labels }} - build-args: | - GO_RUNTIME_IMAGE=${{ env.GO_RUNTIME_IMAGE }} - NODE_BUILDER_IMAGE=${{ env.NODE_BUILDER_IMAGE }} - NODE_RUNTIME_IMAGE=${{ env.NODE_RUNTIME_IMAGE }} - CONTENTLAYER_BUILD=true - - - name: Record digest - env: - IMAGE_DIGEST: ${{ steps.build.outputs.digest }} - OUTPUT_FILE: digest-${{ matrix.service.name }}-${{ matrix.arch.artifact }}.txt - run: bash .github/scripts/build-images/record-digest.sh - - - uses: actions/upload-artifact@v4 - with: - name: digest-${{ matrix.service.name }}-${{ matrix.arch.artifact }} - path: digest-${{ matrix.service.name }}-${{ matrix.arch.artifact }}.txt diff --git a/.github/workflows/check-image.yaml b/.github/workflows/check-image.yaml deleted file mode 100644 index 8c37f19..0000000 --- a/.github/workflows/check-image.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: Check XControl Image Ready - -on: - workflow_dispatch: - inputs: - tag: - required: false - default: latest - -permissions: - contents: read - packages: read - -jobs: - check: - runs-on: ubuntu-latest - steps: - - name: Authenticate to GHCR - uses: ./.github/actions/docker-login - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Check images exist and are pullable - env: - TAG: ${{ inputs.tag }} - IMAGE_LIST_FILE: .github/scripts/check-image/images.txt - run: bash .github/scripts/check-image/check-images.sh diff --git a/.github/workflows/service_release_frontend-deploy.yml b/.github/workflows/service_release_frontend-deploy.yml deleted file mode 100644 index c11abdd..0000000 --- a/.github/workflows/service_release_frontend-deploy.yml +++ /dev/null @@ -1,162 +0,0 @@ -name: Service Release Frontend Deploy - -on: - workflow_dispatch: - inputs: - image_tag: - description: Optional image tag override. Defaults to the current commit SHA. - required: false - type: string - push: - branches: - - main - paths: - - ".github/workflows/service_release_frontend-deploy.yml" - - "deploy/single-node/**" - - "scripts/github-actions/**" - - "src/**" - - "public/**" - - "scripts/**" - - "config/**" - - "package.json" - - "Dockerfile" - - ".env.example" - - "next.config.mjs" - - "tailwind.config.js" - - "postcss.config.mjs" - - "tsconfig.json" - - "contentlayer.config.ts" - -concurrency: - group: frontend-prod - cancel-in-progress: true - -permissions: - contents: read - packages: write - -env: - DEPLOY_HOST: 47.120.61.35 - DEPLOY_USER: root - DEPLOY_DIR: /opt/console-svc-plus - PRIMARY_DOMAIN: cn.svc.plus - SECONDARY_DOMAIN: cn.onwalk.net - GHCR_REGISTRY: ghcr.io - -jobs: - stage-1-build-image: - name: "1. Build and push frontend image" - runs-on: ubuntu-latest - environment: production - outputs: - ghcr_namespace: ${{ steps.meta.outputs.ghcr_namespace }} - image_tag: ${{ steps.meta.outputs.image_tag }} - image_ref: ${{ steps.meta.outputs.image_ref }} - steps: - - name: Compute image metadata - id: meta - run: bash scripts/github-actions/compute-frontend-release-metadata.sh "${{ github.event.inputs.image_tag }}" - - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 - - - name: Clone knowledge content - run: git clone --depth=1 https://github.com/Cloud-Neutral-Workshop/knowledge.git knowledge - - - uses: ./.github/actions/docker-login - with: - registry: ${{ env.GHCR_REGISTRY }} - username: ${{ github.actor }} - password: ${{ github.token }} - - - uses: ./.github/actions/docker-setup-buildx - - - name: Build and push frontend image - uses: ./.github/actions/docker-build-push - with: - context: . - file: Dockerfile - platforms: linux/amd64 - push: true - tags: ${{ steps.meta.outputs.image_ref }} - build-args: | - NODE_BUILDER_IMAGE=node:22-bookworm - NODE_RUNTIME_IMAGE=node:22-slim - CONTENTLAYER_BUILD=true - NEXT_PUBLIC_APP_BASE_URL=${{ vars.NEXT_PUBLIC_APP_BASE_URL || format('https://{0}', env.PRIMARY_DOMAIN) }} - NEXT_PUBLIC_SITE_URL=${{ vars.NEXT_PUBLIC_SITE_URL || format('https://{0}', env.PRIMARY_DOMAIN) }} - NEXT_PUBLIC_LOGIN_URL=${{ vars.NEXT_PUBLIC_LOGIN_URL || format('https://{0}/login', env.PRIMARY_DOMAIN) }} - NEXT_PUBLIC_DOCS_BASE_URL=${{ vars.NEXT_PUBLIC_DOCS_BASE_URL || format('https://{0}/docs', env.PRIMARY_DOMAIN) }} - NEXT_PUBLIC_RUNTIME_ENVIRONMENT=${{ vars.NEXT_PUBLIC_RUNTIME_ENVIRONMENT || 'prod' }} - NEXT_PUBLIC_RUNTIME_REGION=${{ vars.NEXT_PUBLIC_RUNTIME_REGION || 'cn' }} - NEXT_PUBLIC_GISCUS_REPO=${{ vars.NEXT_PUBLIC_GISCUS_REPO || 'cloud-neutral-toolkit/console.svc.plus' }} - NEXT_PUBLIC_GISCUS_REPO_ID=${{ vars.NEXT_PUBLIC_GISCUS_REPO_ID }} - NEXT_PUBLIC_GISCUS_CATEGORY=${{ vars.NEXT_PUBLIC_GISCUS_CATEGORY || 'General' }} - NEXT_PUBLIC_GISCUS_CATEGORY_ID=${{ vars.NEXT_PUBLIC_GISCUS_CATEGORY_ID }} - NEXT_PUBLIC_PAYPAL_CLIENT_ID=${{ vars.NEXT_PUBLIC_PAYPAL_CLIENT_ID }} - NEXT_PUBLIC_STRIPE_PRICE_XSTREAM_PAYGO=${{ vars.NEXT_PUBLIC_STRIPE_PRICE_XSTREAM_PAYGO }} - NEXT_PUBLIC_STRIPE_PRICE_XSTREAM_SUBSCRIPTION=${{ vars.NEXT_PUBLIC_STRIPE_PRICE_XSTREAM_SUBSCRIPTION }} - NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_PAYGO=${{ vars.NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_PAYGO }} - NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION=${{ vars.NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION }} - NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO=${{ vars.NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO }} - NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION=${{ vars.NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION }} - - stage-2-deploy: - name: "2. Deploy frontend stack" - runs-on: ubuntu-latest - needs: stage-1-build-image - environment: production - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 - - - name: Deploy frontend stack - env: - GHCR_USERNAME: ${{ github.actor }} - GHCR_PASSWORD: ${{ github.token }} - SSH_PRIVATE_KEY: ${{ secrets.FRONTEND_DEPLOY_SSH_KEY }} - FRONTEND_IMAGE: ${{ needs.stage-1-build-image.outputs.image_ref }} - APP_BASE_URL: ${{ vars.APP_BASE_URL || format('https://{0}', env.PRIMARY_DOMAIN) }} - NEXT_PUBLIC_APP_BASE_URL: ${{ vars.NEXT_PUBLIC_APP_BASE_URL || format('https://{0}', env.PRIMARY_DOMAIN) }} - NEXT_PUBLIC_SITE_URL: ${{ vars.NEXT_PUBLIC_SITE_URL || format('https://{0}', env.PRIMARY_DOMAIN) }} - NEXT_PUBLIC_LOGIN_URL: ${{ vars.NEXT_PUBLIC_LOGIN_URL || format('https://{0}/login', env.PRIMARY_DOMAIN) }} - NEXT_PUBLIC_DOCS_BASE_URL: ${{ vars.NEXT_PUBLIC_DOCS_BASE_URL || format('https://{0}/docs', env.PRIMARY_DOMAIN) }} - NEXT_PUBLIC_RUNTIME_ENVIRONMENT: ${{ vars.NEXT_PUBLIC_RUNTIME_ENVIRONMENT || 'prod' }} - NEXT_PUBLIC_RUNTIME_REGION: ${{ vars.NEXT_PUBLIC_RUNTIME_REGION || 'cn' }} - RUNTIME_HOSTNAME: ${{ vars.RUNTIME_HOSTNAME || env.PRIMARY_DOMAIN }} - NEXT_RUNTIME_HOSTNAME: ${{ vars.NEXT_RUNTIME_HOSTNAME || env.PRIMARY_DOMAIN }} - DEPLOYMENT_HOSTNAME: ${{ vars.DEPLOYMENT_HOSTNAME || env.PRIMARY_DOMAIN }} - ACCOUNT_SERVICE_URL: ${{ vars.ACCOUNT_SERVICE_URL || 'https://accounts.svc.plus' }} - NEXT_PUBLIC_ACCOUNT_SERVICE_URL: ${{ vars.NEXT_PUBLIC_ACCOUNT_SERVICE_URL || vars.ACCOUNT_SERVICE_URL || 'https://accounts.svc.plus' }} - SERVER_SERVICE_URL: ${{ vars.SERVER_SERVICE_URL || 'https://api.svc.plus' }} - NEXT_PUBLIC_SERVER_SERVICE_URL: ${{ vars.NEXT_PUBLIC_SERVER_SERVICE_URL || vars.SERVER_SERVICE_URL || 'https://api.svc.plus' }} - SERVER_SERVICE_INTERNAL_URL: ${{ vars.SERVER_SERVICE_INTERNAL_URL }} - ROOT_EMAIL_WHITELIST: ${{ vars.ROOT_EMAIL_WHITELIST || 'admin@svc.plus' }} - OPENCLAW_GATEWAY_REMOTE_URL: ${{ vars.OPENCLAW_GATEWAY_REMOTE_URL }} - OPENCLAW_GATEWAY_TOKEN: ${{ secrets.OPENCLAW_GATEWAY_TOKEN }} - VAULT_SERVER_URL: ${{ vars.VAULT_SERVER_URL }} - VAULT_NAMESPACE: ${{ vars.VAULT_NAMESPACE }} - VAULT_TOKEN: ${{ secrets.VAULT_TOKEN }} - APISIX_AI_GATEWAY_URL: ${{ vars.APISIX_AI_GATEWAY_URL }} - AI_GATEWAY_ACCESS_TOKEN: ${{ secrets.AI_GATEWAY_ACCESS_TOKEN }} - INTERNAL_SERVICE_TOKEN: ${{ secrets.INTERNAL_SERVICE_TOKEN }} - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }} - CLOUDFLARE_WEB_ANALYTICS_SITE_TAG: ${{ vars.CLOUDFLARE_WEB_ANALYTICS_SITE_TAG }} - CLOUDFLARE_ZONE_TAG: ${{ vars.CLOUDFLARE_ZONE_TAG }} - NEXT_PUBLIC_GISCUS_REPO: ${{ vars.NEXT_PUBLIC_GISCUS_REPO || 'cloud-neutral-toolkit/console.svc.plus' }} - NEXT_PUBLIC_GISCUS_REPO_ID: ${{ vars.NEXT_PUBLIC_GISCUS_REPO_ID }} - NEXT_PUBLIC_GISCUS_CATEGORY: ${{ vars.NEXT_PUBLIC_GISCUS_CATEGORY || 'General' }} - NEXT_PUBLIC_GISCUS_CATEGORY_ID: ${{ vars.NEXT_PUBLIC_GISCUS_CATEGORY_ID }} - NEXT_PUBLIC_PAYPAL_CLIENT_ID: ${{ vars.NEXT_PUBLIC_PAYPAL_CLIENT_ID }} - NEXT_PUBLIC_STRIPE_PRICE_XSTREAM_PAYGO: ${{ vars.NEXT_PUBLIC_STRIPE_PRICE_XSTREAM_PAYGO }} - NEXT_PUBLIC_STRIPE_PRICE_XSTREAM_SUBSCRIPTION: ${{ vars.NEXT_PUBLIC_STRIPE_PRICE_XSTREAM_SUBSCRIPTION }} - NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_PAYGO: ${{ vars.NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_PAYGO }} - NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION: ${{ vars.NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION }} - NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO: ${{ vars.NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO }} - NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION: ${{ vars.NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION }} - run: bash scripts/github-actions/deploy-frontend-single-node.sh - - - name: Verify primary domain - run: curl -fsSIL "https://${PRIMARY_DOMAIN}" - - - name: Verify secondary domain redirect - run: curl -fsSIL "https://${SECONDARY_DOMAIN}"