From fa196a2c9424e189b7fcad22edf69cd7fa6fd6ee Mon Sep 17 00:00:00 2001 From: Alyx Date: Mon, 18 Mar 2024 09:52:29 +0100 Subject: [PATCH 1/5] Add Dockerfile --- Dockerfile | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..f4ae366d84 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM busybox:1 AS build +ARG PHANPY_RELEASE_VERSION + +WORKDIR /root/phanpy_release + +RUN wget "https://github.com/cheeaun/phanpy/releases/download/${PHANPY_RELEASE_VERSION}/phanpy-dist.tar.gz" && \ + tar -xvf "phanpy-dist.tar.gz" -C /root/phanpy_release && \ + rm "phanpy-dist.tar.gz" + +# --- +FROM busybox:1 + +# Create a non-root user to own the files and run our server +RUN adduser -D static +USER static +WORKDIR /home/static + +# Copy the static website +# Use the .dockerignore file to control what ends up inside the image! +COPY --chown=static:static --from=build /root/phanpy_release /home/static + +# Run BusyBox httpd +CMD ["httpd", "-f", "-v", "-p", "8080"] From e2228bfc8f19e03566553993157e4d1ef8152564 Mon Sep 17 00:00:00 2001 From: Alyx Date: Fri, 19 Apr 2024 16:55:39 +0200 Subject: [PATCH 2/5] Add .dockerignore --- .dockerignore | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..621e3d18f0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,38 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Custom +.env.dev +phanpy-dist.zip +phanpy-dist.tar.gz + +dist/ +node_modules/ +.github/ +readme-assets/ +README.md +.gitignore +.prettierrc +Dockerfile From 0f5f8dfd0f93ae0d9d0cba4d098950e458dedc3d Mon Sep 17 00:00:00 2001 From: Alyx Date: Fri, 19 Apr 2024 16:59:27 +0200 Subject: [PATCH 3/5] Update Dockerfile: - The build now happens in the container itself, uisng node:20-alpine as the build stage - A special artifacts stage is created (but won't be used unless specifically called) to create the archive files, used as the releases - Switched from httpd to nginx The image is still very light (16.9MB) --- Dockerfile | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/Dockerfile b/Dockerfile index f4ae366d84..85da41e8ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,36 @@ -FROM busybox:1 AS build -ARG PHANPY_RELEASE_VERSION +############################################# +# Install everything to build the application +############################################# +FROM node:20-alpine AS build -WORKDIR /root/phanpy_release +WORKDIR /root/phanpy -RUN wget "https://github.com/cheeaun/phanpy/releases/download/${PHANPY_RELEASE_VERSION}/phanpy-dist.tar.gz" && \ - tar -xvf "phanpy-dist.tar.gz" -C /root/phanpy_release && \ - rm "phanpy-dist.tar.gz" +COPY package.json package-lock.json ./ +RUN npm ci -# --- -FROM busybox:1 +COPY . . +RUN npm run build -# Create a non-root user to own the files and run our server -RUN adduser -D static -USER static -WORKDIR /home/static +################################################## +# Special stage to easily extract the app as a zip +################################################## +FROM alpine:3 AS artifacts -# Copy the static website -# Use the .dockerignore file to control what ends up inside the image! -COPY --chown=static:static --from=build /root/phanpy_release /home/static +WORKDIR /root/phanpy -# Run BusyBox httpd -CMD ["httpd", "-f", "-v", "-p", "8080"] +RUN apk add zip +COPY --from=build /root/phanpy/dist /root/phanpy/dist + +# Outputs: +# - /root/phanpy/latest.zip +# - /root/phanpy/latest.tar.gz +RUN zip -r /root/phanpy/latest.zip dist && \ + tar -czf /root/phanpy/latest.tar.gz dist + +##################################################### +# Copy the static files to a mininal web server image +##################################################### +FROM nginx:1-alpine-slim + +ENV NGINX_ENTRYPOINT_QUIET_LOGS=1 +COPY --chown=static:static --from=build /root/phanpy/dist /usr/share/nginx/html From e694de6255963a7d693337b2bdc832a36afb91c0 Mon Sep 17 00:00:00 2001 From: Alyx Date: Fri, 19 Apr 2024 17:08:30 +0200 Subject: [PATCH 4/5] Add Docker GHA - As the build now happens inside the container, I thought it was a good idea to extract it from the image instead of rebuilding it on the host - QEMU and Buildx are just stuff for multiplatform builds (even if it's just a bunch of static files, I don't think docker is able to detect that the base image (nginx) exists in multiplatform, I might be wrong - Add actions to extract metadata in order to tag the image - Add actions to logins to Docker Hub and GitHub Registry - Build and push to both registry, with our previously created tags - Build only, locally, the "artifacts" stage, where we can extract the zip files. As it's reusing the previous stages, it won't be build twice. - Extract the artifacts so the previous release workflow can keep on going (as it should!) --- .github/workflows/prodtag.yml | 75 ++++++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/.github/workflows/prodtag.yml b/.github/workflows/prodtag.yml index 8a2b4392bb..512b85ba36 100644 --- a/.github/workflows/prodtag.yml +++ b/.github/workflows/prodtag.yml @@ -10,19 +10,84 @@ jobs: runs-on: ubuntu-latest permissions: contents: write + packages: write steps: - uses: actions/checkout@v4 with: ref: production # - run: git tag "`date +%Y.%m.%d`.`git rev-parse --short HEAD`" $(git rev-parse HEAD) # - run: git push --tags - - uses: actions/setup-node@v3 - with: - node-version: 18 - - run: npm ci && npm run build - - run: cd dist && zip -r ../phanpy-dist.zip . && tar -czf ../phanpy-dist.tar.gz . && cd .. + - id: tag_name run: echo ::set-output name=tag_name::$(date +%Y.%m.%d).$(git rev-parse --short HEAD) + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # @cheeaun: If you want to check out other ways to tag your Docker image: + # https://github.com/docker/metadata-action/blob/master/README.md + # I kept "tag_name" as the tag name for the Docker image for now + - name: Extract metadata for the Docker image + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ${{ github.repository }} + ghcr.io/${{ github.repository }} + tags: | + type=raw,value=${{ steps.tag_name.outputs.tag_name }} + + # @cheeaun: I think deploying to Docker Hub and GitHub is a good idea, to always have a fallback + # - name: Login to Docker Hub + # uses: docker/login-action@v3 + # with: + # username: ${{ secrets.DOCKERHUB_USERNAME }} + # password: ${{ secrets.DOCKERHUB_TOKEN }} + + # Source: https://github.com/docker/login-action?tab=readme-ov-file#github-container-registry + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + # @cheeaun: I think this is a good idea to support multiple architectures + # Basically here: any Windows, Mac or Linux computers, and 32-bits Raspberry Pi + platforms: linux/amd64,linux/arm64,linux/arm/v7 + push: true + load: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Extract artifacts from the Docker image + uses: docker/build-push-action@v5 + with: + context: . + # @cheeaun: And this is where we extract the artifacts from the Docker image + # The reason I'm extracting it this way, is that you don't depend on anything else than docker, + # and you don't always know if your CI runner will have the tools to zip or tar a directory. + push: false + load: true + tags: ${{ github.repository }}:artifacts-latest + cache-from: type=gha + cache-to: type=gha,mode=max + + # Copy the artifacts files from the Docker container to the host + - run: | + docker create --name phanpy-artifacts ${{ github.repository }}:artifacts-latest + docker cp -q phanpy-artifacts:/root/phanpy/latest.zip ./dist/phanpy-dist.zip + docker cp -q phanpy-artifacts:/root/phanpy/latest.tar.gz ./dist/phanpy-dist.tar.gz + docker rm phanpy-artifacts + - uses: softprops/action-gh-release@v1 with: tag_name: ${{ steps.tag_name.outputs.tag_name }} From 9721925e28e369714103e50449a4c738baec18bc Mon Sep 17 00:00:00 2001 From: Alyx Date: Fri, 19 Apr 2024 17:18:31 +0200 Subject: [PATCH 5/5] Add basic instruction to run the container --- README.md | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3a02321d95..d4a2fe63e0 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@
-Phanpy -=== +# Phanpy **Minimalistic opinionated Mastodon web client.** +
![Fancy screenshot](readme-assets/fancy-screenshot.jpg) @@ -55,7 +55,7 @@ Everything is designed and engineered following my taste and vision. This is a p - On the timeline, the user name is displayed as `[NAME] @[username]`. - For the `@[username]`, always exclude the instance domain name. -- If the `[NAME]` *looks the same* as the `@[username]`, then the `@[username]` is excluded as well. +- If the `[NAME]` _looks the same_ as the `@[username]`, then the `@[username]` is excluded as well. ### Boosts Carousel @@ -123,17 +123,29 @@ Some of these may change in the future. The front-end world is ever-changing. This is a **pure static web app**. You can host it anywhere you want. -Two ways (choose one): +Some examples: -### Easy way +### Using pre-built releases Go to [Releases](https://github.com/cheeaun/phanpy/releases) and download the latest `phanpy-dist.zip` or `phanpy-dist.tar.gz`. It's pre-built so don't need to run any install/build commands. Extract it. Serve the folder of extracted files. +### Using a Docker image + +In your terminal, run: + +``` +$ docker run -d -p 8080:80 cheeaun/phanpy +``` + +Go to http://localhost:8080 and 🎉 + +Make sure to deploy the web app using a reverse proxy that make the connection secure (using HTTPS). + ### Custom-build way Requires [Node.js](https://nodejs.org/). -Download or `git clone` this repository. Use `production` branch for *stable* releases, `main` for *latest*. Build it by running `npm run build` (after `npm install`). Serve the `dist` folder. +Download or `git clone` this repository. Use `production` branch for _stable_ releases, `main` for _latest_. Build it by running `npm run build` (after `npm install`). Serve the `dist` folder. Customization can be done by passing environment variables to the build command. Examples: @@ -214,7 +226,7 @@ Costs involved in running and developing this web app: ## Mascot -[Phanpy](https://bulbapedia.bulbagarden.net/wiki/Phanpy_(Pok%C3%A9mon)) is a Ground-type Pokémon. +[Phanpy]() is a Ground-type Pokémon. ## Maintainers + contributors