diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..d548ba8 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,52 @@ +name: Build + +on: + workflow_call: + inputs: + version: + description: 'Version to build' + required: true + type: string + outputs: + build_run_id: + description: 'Run ID of the build workflow' + value: ${{ jobs.build.outputs.run_id }} + +jobs: + build: + runs-on: ubuntu-latest + outputs: + run_id: ${{ steps.build-meta.outputs.run_id }} + steps: + - uses: actions/checkout@v4 + + - name: Export build run id + id: build-meta + run: echo "run_id=${{ github.run_id }}" >> $GITHUB_OUTPUT + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build image + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + APP_VERSION=${{ inputs.version }} + tags: | + liveboxmonitor:${{ inputs.version }} + outputs: type=oci,dest=./image.tar + push: false + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: image-${{ inputs.version }} + path: ./image.tar + retention-days: 1 diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml new file mode 100644 index 0000000..53fd379 --- /dev/null +++ b/.github/workflows/cron.yml @@ -0,0 +1,83 @@ +name: Release Workflow + +on: + schedule: + - cron: "0 2 * * *" # Run at 2:00 AM every day + pull_request: + branches: + - main + workflow_dispatch: + inputs: + release: + description: 'Release new version if true' + required: false + default: false + type: boolean + +permissions: + contents: write + packages: write + actions: read + +jobs: + extract-version: + runs-on: ubuntu-latest + name: Extract Version + outputs: + versions_differ: ${{ steps.compare_versions.outputs.versions_differ }} + lbm_version: ${{ steps.get_lbm_version.outputs.lbm_version }} + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Get LiveboxMonitor current version + id: get_lbm_version + run: | + LBM_VERSION=$(curl -s https://api.github.com/repos/p-dor/LiveboxMonitor/releases/latest | jq -r '.tag_name') + echo "Current LiveboxMonitor version is $LBM_VERSION" + echo "lbm_version=$LBM_VERSION" >> $GITHUB_OUTPUT + - name: Get latest tag + id: get_latest_tag + run: | + LATEST_TAG=$(git describe --tags $(git rev-list --tags --max-count=1)) + LATEST_VERSION="${LATEST_TAG#v}" + echo "Latest tag is $LATEST_TAG (version $LATEST_VERSION)" + echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT + echo "latest_version=$LATEST_VERSION" >> $GITHUB_OUTPUT + - name: Compare versions + id: compare_versions + run: | + echo "Comparing LiveboxMonitor version ${{ steps.get_lbm_version.outputs.lbm_version }} with latest tag version ${{ steps.get_latest_tag.outputs.latest_version }}" + if [ "${{ steps.get_lbm_version.outputs.lbm_version }}" != "${{ steps.get_latest_tag.outputs.latest_version }}" ]; then + echo "versions_differ=true" >> $GITHUB_OUTPUT + else + echo "versions_differ=false" >> $GITHUB_OUTPUT + fi + + build: + uses: ./.github/workflows/build.yaml + if: needs.extract-version.outputs.versions_differ == 'true' || + github.event_name == 'pull_request' + needs: extract-version + with: + version: ${{ needs.extract-version.outputs.lbm_version }} + + test: + uses: ./.github/workflows/test.yaml + if: needs.extract-version.outputs.versions_differ == 'true' || + github.event_name == 'pull_request' + needs: [build, extract-version] + with: + version: ${{ needs.extract-version.outputs.lbm_version }} + build_run_id: ${{ needs.build.outputs.build_run_id }} + + release: + uses: ./.github/workflows/publish.yaml + if: github.event_name != 'pull_request' && + (github.event_name == 'workflow_dispatch' && github.event.inputs.release == 'true') && + needs.extract-version.outputs.versions_differ == 'true' + needs: [build, test, extract-version] + with: + version: ${{ needs.extract-version.outputs.lbm_version }} + build_run_id: ${{ needs.build.outputs.build_run_id }} + secrets: inherit diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..5d13123 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,110 @@ +name: Publish + +on: + workflow_call: + inputs: + version: + description: 'Version to publish' + required: true + type: string + build_run_id: + description: 'Run ID of the build workflow' + required: true + type: string + +env: + REGISTRY_GHCR: ghcr.io + REGISTRY_DOCKER: docker.io + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: image-${{ inputs.version }} + github-token: ${{ secrets.GITHUB_TOKEN }} + repository: ${{ github.repository }} + run-id: ${{ inputs.build_run_id }} + path: . + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY_GHCR }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY_DOCKER }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Load image from artifact + run: | + docker load --input ./image.tar + + - name: Push to GitHub Container Registry + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: | + ${{ env.REGISTRY_GHCR }}/${{ github.repository_owner }}/liveboxmonitor:${{ inputs.version }} + ${{ env.REGISTRY_GHCR }}/${{ github.repository_owner }}/liveboxmonitor:latest + + - name: Push to Docker Hub + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: | + ${{ env.REGISTRY_DOCKER }}/${{ secrets.DOCKER_USERNAME }}/liveboxmonitor:${{ inputs.version }} + ${{ env.REGISTRY_DOCKER }}/${{ secrets.DOCKER_USERNAME }}/liveboxmonitor:latest + + - name: Create Release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.version.outputs.tag }} + release_name: Release ${{ steps.version.outputs.version }} + body: | + # LiveboxMonitor Container v${{ steps.version.outputs.version }} + + Built from: [${{ github.sha }}](https://github.com/${{ github.repository }}/commit/${{ github.sha }}) + + ## Images Available at : + + ### GitHub Container Registry + + - `/${{ secrets.DOCKER_USERNAME }}/liveboxmonitor:${{ steps.version.outputs.version }}` + - `/${{ secrets.DOCKER_USERNAME }}/liveboxmonitor:latest` + + ### Docker Hub + + - `/${{ secrets.DOCKER_USERNAME }}/liveboxmonitor:${{ steps.version.outputs.version }}` + - `/${{ secrets.DOCKER_USERNAME }}/liveboxmonitor:latest` + + ## Upstream Project + - **LiveboxMonitor**: https://github.com/p-dor/LiveboxMonitor + draft: false + prerelease: false diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..0639d80 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,105 @@ +name: Test + +on: + workflow_call: + inputs: + version: + description: "Version to test" + required: true + type: string + build_run_id: + description: "Run ID of the build workflow" + required: true + type: string + +jobs: + download-and-test: + runs-on: ubuntu-latest + permissions: + contents: read + actions: read + + strategy: + fail-fast: false + matrix: + platform: [amd64, arm64] + + steps: + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: image-${{ inputs.version }} + github-token: ${{ secrets.GITHUB_TOKEN }} + repository: ${{ github.repository }} + run-id: ${{ inputs.build_run_id }} + path: ./artifact + + - name: Show downloaded files + run: ls -lah ./artifact + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Install skopeo + run: | + sudo apt-get update + sudo apt-get install -y skopeo + + - name: Import image for ${{ matrix.platform }} into Docker + run: | + set -euo pipefail + IMAGE_TAG="liveboxmonitor:${{ inputs.version }}" + + # Extract the linux/${{ matrix.platform }} variant from the OCI archive + skopeo copy \ + --override-os linux \ + --override-arch ${{ matrix.platform }} \ + oci-archive:./artifact/image.tar \ + docker-daemon:${IMAGE_TAG} + + docker image inspect "${IMAGE_TAG}" >/dev/null + echo "Imported ${IMAGE_TAG} for arch ${{ matrix.platform }}" + + - name: Run healthcheck + shell: bash + run: | + set -euo pipefail + IMAGE_TAG="liveboxmonitor:${{ inputs.version }}" + NAME="liveboxmonitor-test-${{ matrix.platform }}" + + # Verify that the image has a HEALTHCHECK (otherwise the test can never pass) + HAS_HEALTH=$(docker image inspect -f '{{if .Config.Healthcheck}}yes{{else}}no{{end}}' "${IMAGE_TAG}") + if [ "${HAS_HEALTH}" != "yes" ]; then + echo "No HEALTHCHECK defined in the image -> failing test." + exit 1 + fi + + docker run -d --rm --platform "linux/${{ matrix.platform }}" --name "${NAME}" "${IMAGE_TAG}" + echo "Started container: ${NAME}" + + MAX_ATTEMPTS=20 + for ATTEMPT in $(seq 1 ${MAX_ATTEMPTS}); do + STATUS=$(docker inspect -f '{{.State.Status}}' "${NAME}" || true) + HEALTH=$(docker inspect -f '{{.State.Health.Status}}' "${NAME}" 2>/dev/null || echo "unknown") + + echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: status=${STATUS} health=${HEALTH}" + + if [ "${STATUS}" != "running" ]; then + echo "Container exited. Logs:" + docker logs "${NAME}" || true + exit 1 + fi + + if [ "${HEALTH}" = "healthy" ]; then + echo "Container is healthy!" + docker stop "${NAME}" + exit 0 + fi + + sleep 5 + done + + echo "Timeout waiting for healthy. Logs:" + docker logs "${NAME}" || true + docker stop "${NAME}" || true + exit 1