Build Nightly #958
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" |