Files
TehRiehlDeal 7b60af21ae
frontend-ci / secrets-scan (push) Successful in 5s
frontend-ci / sast (push) Successful in 9s
frontend-ci / fs-scan (push) Successful in 11s
frontend-ci / typecheck (push) Successful in 14s
frontend-ci / lint (push) Successful in 15s
frontend-ci / build (push) Successful in 38s
frontend-ci / push (push) Successful in 37s
fix(ci): read HARBOR_HOST from vars, not secrets
Mirror the backend fix. HARBOR_HOST is a Gitea Actions variable, not
a secret; secrets.HARBOR_HOST was empty the whole time. Use
vars.HARBOR_HOST and drop the now-pointless protocol-strip defense.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 13:09:49 -07:00

201 lines
7.6 KiB
YAML

name: frontend-ci
on:
push:
branches: ["**"]
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: package-lock.json
- run: npm ci
- run: npx eslint .
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: package-lock.json
- run: npm ci
- run: npx tsc -b
secrets-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install and run gitleaks
run: |
GL_VERSION=8.18.4
curl -sSL "https://github.com/gitleaks/gitleaks/releases/download/v${GL_VERSION}/gitleaks_${GL_VERSION}_linux_x64.tar.gz" \
| tar xz -C /tmp gitleaks
/tmp/gitleaks detect --redact --no-banner --verbose --source .
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: returntocorp/semgrep-action@v1
with:
config: "p/auto"
fs-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install and run Trivy (filesystem)
run: |
TRIVY_VERSION=0.70.0
curl -sSL "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" \
| tar xz -C /tmp trivy
/tmp/trivy fs --severity HIGH,CRITICAL --exit-code 1 --ignore-unfixed --no-progress .
build:
runs-on: ubuntu-latest
needs: [lint, typecheck]
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile
target: production
tags: movieloop-frontend:ci-${{ github.sha }}
load: true
- name: Install and run Trivy (image)
run: |
TRIVY_VERSION=0.70.0
curl -sSL "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" \
| tar xz -C /tmp trivy
/tmp/trivy image --severity HIGH,CRITICAL --exit-code 1 --ignore-unfixed --no-progress \
movieloop-frontend:ci-${{ github.sha }}
push:
runs-on: ubuntu-latest
needs: [build, secrets-scan, sast, fs-scan]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
env:
# HARBOR_HOST is a Gitea Actions *variable* (not a secret) — vars and
# secrets are separate stores and don't cross over.
HARBOR_HOST: ${{ vars.HARBOR_HOST }}
HARBOR_PROJECT: movieloop
IMAGE_NAME: frontend
steps:
- uses: actions/checkout@v4
with:
# Need full history so the back-pushed git tag can be created against
# the right commit, and so token-auth on push to origin works.
fetch-depth: 0
- uses: docker/setup-buildx-action@v3
- name: Compute tag inputs
run: |
set -u
: "${HARBOR_HOST:?HARBOR_HOST is empty — set it as a Gitea Actions variable (not a secret).}"
SHA_SHORT=$(git rev-parse --short HEAD)
VERSION=$(jq -r .version package.json)
echo "SHA_SHORT=${SHA_SHORT}" >> "$GITHUB_ENV"
echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
echo "HARBOR_HOST=${HARBOR_HOST}, VERSION=${VERSION}, SHA_SHORT=${SHA_SHORT}"
- name: Refuse to overwrite an existing version tag in Harbor
env:
HARBOR_USERNAME: ${{ secrets.MOVIELOOP_USERNAME }}
HARBOR_PASSWORD: ${{ secrets.MOVIELOOP_PASSWORD }}
run: |
set -u
url="https://${HARBOR_HOST}/api/v2.0/projects/${HARBOR_PROJECT}/repositories/${IMAGE_NAME}/artifacts/${VERSION}/tags"
echo "Checking: ${url}"
code=$(curl -s -o /dev/null -w "%{http_code}" -u "${HARBOR_USERNAME}:${HARBOR_PASSWORD}" "${url}" || echo "000")
echo "HTTP status: ${code}"
case "$code" in
200)
echo "::error::Tag ${HARBOR_PROJECT}/${IMAGE_NAME}:${VERSION} already exists in Harbor. Bump package.json before merging."
exit 1
;;
404)
echo "Version ${VERSION} not yet published — proceeding."
;;
000)
echo "::error::curl could not reach https://${HARBOR_HOST} (likely DNS or network). Verify this runner can resolve the host."
exit 1
;;
401|403)
echo "::error::Harbor auth failed (HTTP ${code}). Check MOVIELOOP_USERNAME / MOVIELOOP_PASSWORD and that the robot account has read access on project '${HARBOR_PROJECT}'."
exit 1
;;
*)
echo "::warning::Unexpected status ${code} from Harbor — proceeding."
;;
esac
- name: Log in to Harbor
env:
HARBOR_USERNAME: ${{ secrets.MOVIELOOP_USERNAME }}
HARBOR_PASSWORD: ${{ secrets.MOVIELOOP_PASSWORD }}
run: |
echo "${HARBOR_PASSWORD}" | docker login "${HARBOR_HOST}" -u "${HARBOR_USERNAME}" --password-stdin
- name: Build, tag, and push image
run: |
set -eu
REPO="${HARBOR_HOST}/${HARBOR_PROJECT}/${IMAGE_NAME}"
docker buildx build --load --target production -t movieloop-${IMAGE_NAME}:${SHA_SHORT} -f Dockerfile .
docker tag movieloop-${IMAGE_NAME}:${SHA_SHORT} ${REPO}:${VERSION}
docker tag movieloop-${IMAGE_NAME}:${SHA_SHORT} ${REPO}:${SHA_SHORT}
docker tag movieloop-${IMAGE_NAME}:${SHA_SHORT} ${REPO}:latest
docker push ${REPO}:${VERSION}
docker push ${REPO}:${SHA_SHORT}
docker push ${REPO}:latest
- name: Install cosign
run: |
curl -fsSL "https://github.com/sigstore/cosign/releases/download/v2.4.1/cosign-linux-amd64" \
-o /usr/local/bin/cosign
chmod +x /usr/local/bin/cosign
cosign version
- name: Sign image with cosign
env:
COSIGN_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
run: |
set -eu
# Sign by SHA tag — cosign resolves to the underlying digest, so a
# single signature covers every tag pointing at the same digest
# (version, sha, latest). Harbor looks up signatures by digest.
# Reuses the docker login from earlier in this job for registry auth.
cosign sign --key env://COSIGN_KEY --yes \
"${HARBOR_HOST}/${HARBOR_PROJECT}/${IMAGE_NAME}:${SHA_SHORT}"
- name: Push back git tag
env:
# Auto-injected per-run token; has push permission to this repo.
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -eu
git config user.name "gitea-actions[bot]"
git config user.email "gitea-actions[bot]@tehriehldeal.com"
git tag "v${VERSION}" -m "${IMAGE_NAME} v${VERSION}" 2>/dev/null || true
ORIGIN="$(git remote get-url origin | sed -E "s#https?://([^/]+)#https://${GITEA_TOKEN}@\1#")"
# A failed tag push shouldn't undo a successful image push — log
# and proceed so we don't poison subsequent retries.
git push "$ORIGIN" --tags || echo "::warning::Tag push to origin failed — image is pushed; create the tag manually if needed."
- name: Log out of Harbor
if: always()
run: docker logout "${HARBOR_HOST}" || true