diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..8d777f3e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +**/* +!package.json +!package-lock.json +!guide +!nginx.conf \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..b899086f --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +DOCKER_REGISTRY=registry.mydomain.com \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index c024b93c..00000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,29 +0,0 @@ -# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs - -name: Vuepress Build Test - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [18.x] - - steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' - - run: npm ci - - run: npm run build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..1760ce51 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,67 @@ +name: Build and Release Docker Image + +on: + push: + branches: + - main + tags: + - 'v*' + pull_request: + branches: + - main + +env: + REGISTRY: ${{ secrets.DOCKER_REGISTRY || 'docker.io' }} + IMAGE_NAME: raspdevpy/${{ github.event.repository.name }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Private Registry + if: ${{ github.event_name != 'pull_request' }} + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr,prefix=pr- + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + timeout-minutes: 20 + with: + context: . + platforms: linux/amd64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + BUILD_DATE=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }} + GIT_COMMIT=${{ github.sha }} + VERSION=${{ steps.meta.outputs.version }} + cache-from: ${{ github.event_name == 'pull_request' && 'type=local,src=/tmp/.buildx-cache' || format('type=registry,ref={0}/{1}:cache', env.REGISTRY, env.IMAGE_NAME) }} + cache-to: ${{ github.event_name == 'pull_request' && 'type=local,dest=/tmp/.buildx-cache-new,mode=max' || format('type=registry,ref={0}/{1}:cache,mode=max', env.REGISTRY, env.IMAGE_NAME) }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 233e2636..9df3a141 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ ignore/* .DS_Store guide/.vuepress/public/images/* .vscode -OnTemplate.md \ No newline at end of file +OnTemplate.md +.env \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..4eca718a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +# Stage 1 — build the static assets +FROM node:20-alpine3.21 AS builder +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY . . +# adjust the build command if your project uses a different script +RUN npm run build + +# Stage 2 — serve with nginx +FROM nginx:1.25-alpine +ARG BUILD_DIR=guide/.vuepress/dist + +# Remove default config and add a simple SPA-friendly config +COPY nginx.conf /etc/nginx/conf.d/default.conf + +# Copy built static files from the builder stage +COPY --from=builder /app/${BUILD_DIR} /usr/share/nginx/html + +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..31841ebb --- /dev/null +++ b/Makefile @@ -0,0 +1,65 @@ +include .env + +# Variables +# DOCKER_REGISTRY ?= +DOCKER_REPO ?= raspdevpy/$(shell basename `git remote get-url origin`) +VERSION ?= $(shell git describe --tags --always --dirty) +LATEST_TAG := latest +BUILD_DATE := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") +GIT_COMMIT := $(shell git rev-parse HEAD) + +# Docker image tags +IMAGE_TAG := $(DOCKER_REGISTRY)/$(DOCKER_REPO):$(VERSION) +LATEST_IMAGE := $(DOCKER_REGISTRY)/$(DOCKER_REPO):$(LATEST_TAG) + +.PHONY: help build push release clean login test + +help: ## Show this help message + @echo "Available targets:" + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-15s %s\n", $$1, $$2}' $(MAKEFILE_LIST) + +build: ## Build Docker image + @echo "Building Docker image: $(IMAGE_TAG)" + docker build \ + --build-arg BUILD_DATE=$(BUILD_DATE) \ + --build-arg GIT_COMMIT=$(GIT_COMMIT) \ + --build-arg VERSION=$(VERSION) \ + -t $(IMAGE_TAG) \ + -t $(LATEST_IMAGE) \ + . + @echo "Built: $(IMAGE_TAG)" + @echo "Built: $(LATEST_IMAGE)" + +test: ## Test the Docker image + @echo "Testing Docker image: $(IMAGE_TAG)" + # docker run --rm $(IMAGE_TAG) curl http://localhost:80/index.html + +test-interactive: ## Run an interactive shell in the Docker image for testing + @echo "Starting interactive shell in Docker image: $(IMAGE_TAG)" + docker run --rm -it $(IMAGE_TAG) sh + +login: ## Login to Docker registry + @echo "Logging into Docker registry: $(DOCKER_REGISTRY)" + docker login $(DOCKER_REGISTRY) + +push: ## Push Docker image to registry + @echo "Pushing Docker image: $(IMAGE_TAG)" + docker push $(IMAGE_TAG) + docker push $(LATEST_IMAGE) + @echo "Pushed: $(IMAGE_TAG)" + @echo "Pushed: $(LATEST_IMAGE)" + +release: build test push ## Build, test and push Docker image + @echo "Release completed for version: $(VERSION)" + +clean: ## Remove local Docker images + @echo "Cleaning up local Docker images" + -docker rmi $(IMAGE_TAG) 2>/dev/null || true + -docker rmi $(LATEST_IMAGE) 2>/dev/null || true + +# CI/CD helpers +ci-info: ## Show build information for CI + @echo "VERSION=$(VERSION)" + @echo "IMAGE_TAG=$(IMAGE_TAG)" + @echo "BUILD_DATE=$(BUILD_DATE)" + @echo "GIT_COMMIT=$(GIT_COMMIT)" \ No newline at end of file diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 00000000..62702288 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,11 @@ +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } +} \ No newline at end of file