Skip to content

Build Nightly

Build Nightly #958

Workflow file for this run

name: Build Nightly
run-name: Build Nightly ${{ inputs.dry_run && '(🧪 Dry-Run)' || '' }}
on:
schedule:
- cron: "0 10 * * *"
workflow_dispatch:
inputs:
dry_run:
description: "Build without publishing"
type: boolean
default: true
permissions:
contents: read
concurrency:
group: build-nightly
cancel-in-progress: false
env:
DIST_DIR: /tmp/builds
DIGEST_DIR: /tmp/digests
DOCKER_IMG: artalk/artalk-go
jobs:
prepare:
runs-on: ubuntu-24.04
outputs:
skip: ${{ steps.check.outputs.skip }}
steps:
- name: Checkout Code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check whether the scheduled build should run
id: check
if: github.event_name == 'schedule'
run: |
if [[ "$(git log --since='24 hours ago' --oneline | wc -l)" -eq 0 ]] || \
[[ "$GITHUB_REPOSITORY" != "ArtalkJS/Artalk" ]]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
fi
ui:
name: Build Nightly UI assets
if: ${{ needs.prepare.outputs.skip != 'true' }}
needs: prepare
runs-on: ubuntu-24.04
steps:
- name: Checkout Code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Setup pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
with:
version: 10.33.2
- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22.x
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build UI
id: build_ui
run: |
make build-frontend
echo "ui_tarball=$(make -s pack-frontend)" >> "$GITHUB_OUTPUT"
- name: Upload UI assets
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: nightly-ui-src
path: |
public
ui/artalk/dist
if-no-files-found: error
retention-days: 1
- name: Upload UI tarball
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: build-ui-tarball
path: ${{ steps.build_ui.outputs.ui_tarball }}
if-no-files-found: error
retention-days: 1
build:
name: Build Nightly (${{ matrix.platform }})
needs: ui
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-24.04
platform: linux/amd64
platform_pair: linux_amd64
- runner: ubuntu-24.04-arm
platform: linux/arm64
platform_pair: linux_arm64
steps:
- name: Checkout Code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Download UI assets
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: nightly-ui-src
- name: Generate Docker metadata
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
with:
images: ${{ env.DOCKER_IMG }}
tags: type=raw,value=nightly-build-${{ matrix.platform_pair }}
- name: Login to DockerHub
if: ${{ github.event_name == 'schedule' || !inputs.dry_run }}
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
- name: Build dry-run image
if: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run }}
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
context: .
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
APP_COMMIT_HASH=${{ github.sha }}
SKIP_UI_BUILD=true
outputs: type=docker,name=${{ steps.meta.outputs.tags }}
cache-from: type=gha,scope=nightly-${{ matrix.platform_pair }}
cache-to: type=gha,mode=max,scope=nightly-${{ matrix.platform_pair }}
- name: Build and push image by digest
if: ${{ github.event_name == 'schedule' || !inputs.dry_run }}
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
context: .
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
APP_COMMIT_HASH=${{ github.sha }}
SKIP_UI_BUILD=true
outputs: |
type=docker,name=${{ steps.meta.outputs.tags }}
type=image,push=true,name=${{ env.DOCKER_IMG }},push-by-digest=true,name-canonical=true
cache-from: type=gha,scope=nightly-${{ matrix.platform_pair }}
cache-to: type=gha,mode=max,scope=nightly-${{ matrix.platform_pair }}
- name: Smoke test image
env:
IMAGE: ${{ steps.meta.outputs.tags }}
run: docker run --rm --entrypoint /artalk "$IMAGE" version
- name: Export digest
if: ${{ github.event_name == 'schedule' || !inputs.dry_run }}
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: |
mkdir -p "$DIGEST_DIR"
touch "$DIGEST_DIR/${DIGEST#sha256:}"
- name: Upload digest
if: ${{ github.event_name == 'schedule' || !inputs.dry_run }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: nightly-digest-${{ matrix.platform_pair }}
path: ${{ env.DIGEST_DIR }}/*
if-no-files-found: error
retention-days: 1
- name: Export Docker image
id: docker
env:
IMAGE: ${{ steps.meta.outputs.tags }}
PLATFORM_PAIR: ${{ matrix.platform_pair }}
run: |
image_file="artalk_docker_nigthly_$(date +'%Y%m%d')_${PLATFORM_PAIR}.tar.gz"
docker save "$IMAGE" | gzip > "$image_file"
echo "image_file=$image_file" >> "$GITHUB_OUTPUT"
- name: Pack app
id: app
env:
IMAGE: ${{ steps.meta.outputs.tags }}
PLATFORM_PAIR: ${{ matrix.platform_pair }}
run: |
base_dir="/tmp/app_pkg"
name="artalk_nigthly_$(date +'%Y%m%d')_${PLATFORM_PAIR}"
package_dir="$base_dir/$name"
package_file="$base_dir/$name.tar.gz"
mkdir -p "$package_dir"
docker run --rm --entrypoint cat "$IMAGE" /artalk > "$package_dir/artalk"
chmod +x "$package_dir/artalk"
cp conf/artalk.example.yml "$package_dir/artalk.yml"
cp README.md LICENSE CHANGELOG.md "$package_dir"
tar -czf "$package_file" -C "$base_dir" "$name"
echo "package_file=$package_file" >> "$GITHUB_OUTPUT"
- name: Collect build artifacts
env:
APP_PACKAGE: ${{ steps.app.outputs.package_file }}
DOCKER_IMAGE: ${{ steps.docker.outputs.image_file }}
run: |
mkdir -p "$DIST_DIR"
mv "$APP_PACKAGE" "$DIST_DIR"
mv "$DOCKER_IMAGE" "$DIST_DIR"
- name: Upload build artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: build-${{ matrix.platform_pair }}
path: ${{ env.DIST_DIR }}/*
if-no-files-found: error
retention-days: 1
docker_merge:
name: Publish Nightly Docker manifest
if: ${{ github.event_name == 'schedule' || !inputs.dry_run }}
needs: build
runs-on: ubuntu-24.04
steps:
- name: Download digests
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: ${{ env.DIGEST_DIR }}
pattern: nightly-digest-*
merge-multiple: true
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
- name: Generate Docker metadata
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
with:
images: ${{ env.DOCKER_IMG }}
tags: type=raw,value=nightly
- name: Login to DockerHub
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Create and push manifest
env:
METADATA: ${{ steps.meta.outputs.json }}
run: |
mapfile -t tags < <(jq -r '.tags[]' <<< "$METADATA")
tag_args=()
for tag in "${tags[@]}"; do
tag_args+=(--tag "$tag")
done
shopt -s nullglob
digest_files=("$DIGEST_DIR"/*)
if [[ "${#digest_files[@]}" -ne 2 ]]; then
echo "Expected 2 platform digests, found ${#digest_files[@]}."
exit 1
fi
sources=()
for digest_file in "${digest_files[@]}"; do
digest="$(basename "$digest_file")"
if [[ ! "$digest" =~ ^[0-9a-f]{64}$ ]]; then
echo "Invalid image digest: $digest"
exit 1
fi
sources+=("$DOCKER_IMG@sha256:$digest")
done
docker buildx imagetools create "${tag_args[@]}" "${sources[@]}"
- name: Inspect image
env:
IMAGE: ${{ env.DOCKER_IMG }}:${{ steps.meta.outputs.version }}
run: docker buildx imagetools inspect "$IMAGE"
release:
name: Publish Nightly GitHub release
if: ${{ github.event_name == 'schedule' || !inputs.dry_run }}
needs: [build, docker_merge]
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- name: Checkout Code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Download builds
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: ${{ env.DIST_DIR }}
pattern: build-*
merge-multiple: true
- name: Setup Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: go.mod
cache: false
- name: Setup git-chglog
run: |
GOBIN="$RUNNER_TEMP/bin" go install github.com/git-chglog/git-chglog/cmd/git-chglog@v0.15.4
echo "$RUNNER_TEMP/bin" >> "$GITHUB_PATH"
go version -m "$RUNNER_TEMP/bin/git-chglog"
- name: Generate changelog
run: |
git-chglog --config .github/chglog/config.yml --next-tag nightly nightly > /tmp/changelog.md
cat >> /tmp/changelog.md <<'EOF'
> [!NOTE]
> The Nightly version is automatically built from the latest code and may be unstable.
> Docker users can run `docker pull artalk/artalk-go:nightly` to get this build.
EOF
- name: Calculate checksums
run: |
cd "$DIST_DIR"
sha256sum -- * > checksums.txt
cat checksums.txt
- name: Move Nightly tag
env:
GH_TOKEN: ${{ github.token }}
run: |
if gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/nightly" > /dev/null 2>&1; then
gh api --method PATCH "repos/$GITHUB_REPOSITORY/git/refs/tags/nightly" \
-f sha="$GITHUB_SHA" -F force=true > /dev/null
else
gh api --method POST "repos/$GITHUB_REPOSITORY/git/refs" \
-f ref=refs/tags/nightly -f sha="$GITHUB_SHA" > /dev/null
fi
tag_sha="$(gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/nightly" --jq '.object.sha')"
if [[ "$tag_sha" != "$GITHUB_SHA" ]]; then
echo "Nightly tag points to $tag_sha, expected $GITHUB_SHA."
exit 1
fi
- name: Publish release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release delete nightly --yes || true
mapfile -d '' release_files < <(find "$DIST_DIR" -type f -print0)
gh release create nightly "${release_files[@]}" \
--title "🧪 Nightly Version" \
--notes-file /tmp/changelog.md \
--prerelease \
--target "$GITHUB_SHA"