Repository navigation
Docker Publish (dev - latest build) #16
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: Publish to Docker Hub | |
| run-name: Docker Publish (${{ inputs.docker_tag || 'auto' }} - ${{ inputs.run_id || 'latest build' }}) | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| docker_tag: | |
| description: "Docker tag to publish" | |
| required: true | |
| type: choice | |
| options: | |
| - dev | |
| - preview | |
| - latest | |
| default: "dev" | |
| run_id: | |
| description: "Build workflow run ID (leave empty to use latest)" | |
| required: false | |
| type: string | |
| use_local_storage: | |
| description: "Use local artifact storage" | |
| required: false | |
| type: boolean | |
| default: true | |
| use_self_hosted_runners: | |
| description: "Use self-hosted runners" | |
| required: false | |
| type: boolean | |
| default: true | |
| workflow_call: | |
| inputs: | |
| docker_tag: | |
| description: "Docker tag to publish (leave empty to derive from the build metadata)" | |
| required: false | |
| type: string | |
| default: "" | |
| run_id: | |
| description: "Build workflow run ID (leave empty to use latest)" | |
| required: false | |
| type: string | |
| use_local_storage: | |
| description: "Use local artifact storage" | |
| required: false | |
| type: boolean | |
| default: true | |
| use_self_hosted_runners: | |
| description: "Use self-hosted runners" | |
| required: false | |
| type: boolean | |
| default: true | |
| permissions: | |
| actions: read | |
| contents: read | |
| jobs: | |
| publish: | |
| name: Publish to Docker Hub | |
| runs-on: ${{ inputs.use_self_hosted_runners && fromJson('["self-hosted","Linux"]') || fromJson('["ubuntu-latest"]') }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| - name: Validate local storage configuration | |
| uses: ./.github/actions/validate-local-storage | |
| with: | |
| use_local_storage: ${{ inputs.use_local_storage }} | |
| artifact_host: ${{ vars.ARTIFACT_HOST }} | |
| artifact_base_path: ${{ vars.ARTIFACT_BASE_PATH }} | |
| - name: Get Latest Build Run ID | |
| id: get-run-id | |
| uses: ./.github/actions/get-latest-build-run-id | |
| with: | |
| run_id: ${{ inputs.run_id }} | |
| github_token: ${{ github.token }} | |
| - name: Download Server Artifact | |
| uses: ./.github/actions/download-artifact | |
| with: | |
| name: Server | |
| path: ./server-publish | |
| github-token: ${{ github.token }} | |
| artifact-host: ${{ inputs.use_local_storage && vars.ARTIFACT_HOST || '' }} | |
| artifact-base-path: ${{ inputs.use_local_storage && vars.ARTIFACT_BASE_PATH || '' }} | |
| run-id: ${{ steps.get-run-id.outputs.run_id }} | |
| - name: Verify Server Payload | |
| uses: ./.github/actions/verify-server-payload | |
| with: | |
| path: ./server-publish | |
| - name: Get Build Metadata | |
| id: metadata | |
| uses: ./.github/actions/get-build-metadata | |
| with: | |
| run-id: ${{ steps.get-run-id.outputs.run_id }} | |
| github-token: ${{ github.token }} | |
| artifact-host: ${{ inputs.use_local_storage && vars.ARTIFACT_HOST || '' }} | |
| artifact-base-path: ${{ inputs.use_local_storage && vars.ARTIFACT_BASE_PATH || '' }} | |
| # Each Linux runtime the build produced becomes one architecture in the image. The | |
| # build metadata decides which runtimes matter; a prerelease build can only go out | |
| # on the 'dev' tag, so a stable image can never be clobbered by a dev build. | |
| - name: Prepare Docker Context | |
| id: payload | |
| shell: pwsh | |
| env: | |
| DOCKER_TAG_INPUT: ${{ inputs.docker_tag }} | |
| PRERELEASE: ${{ steps.metadata.outputs.prerelease }} | |
| RELEASE_VERSION: ${{ steps.metadata.outputs.release_version }} | |
| SERVER_RIDS: ${{ steps.metadata.outputs.server_rids }} | |
| run: | | |
| $ArchByRid = [ordered]@{ | |
| 'linux-x64' = 'amd64' | |
| 'linux-arm64' = 'arm64' | |
| } | |
| # An explicit tag wins; otherwise the build metadata decides, so a prerelease | |
| # build can never be published over the stable 'latest' image. | |
| $DockerTag = $env:DOCKER_TAG_INPUT | |
| if (-not $DockerTag) { | |
| $DockerTag = if ($env:PRERELEASE -eq 'true') { 'dev' } else { 'latest' } | |
| } | |
| if ($env:PRERELEASE -eq 'true' -and $DockerTag -ne 'dev') { | |
| throw "This build was flagged prerelease, so it must publish to the 'dev' tag, not '$DockerTag'. Re-run with docker_tag=dev or rebuild without the prerelease flag." | |
| } | |
| New-Item -Path ./payload -ItemType Directory -Force | Out-Null | |
| $BuildRids = $env:SERVER_RIDS.Split(',') | |
| $Platforms = [System.Collections.Generic.List[string]]::new() | |
| foreach ($Rid in $ArchByRid.Keys) { | |
| if ($BuildRids -notcontains $Rid) { | |
| continue | |
| } | |
| $Arch = $ArchByRid[$Rid] | |
| $Source = "./server-publish/$Rid" | |
| if (-not (Test-Path -PathType Container $Source)) { | |
| throw "'$Source' is listed in the build metadata but is missing from the Server artifact." | |
| } | |
| Copy-Item -Path $Source -Destination "./payload/$Arch" -Recurse -Force | |
| $Platforms.Add("linux/$Arch") | |
| Write-Host "linux/$Arch <- $Rid" | |
| } | |
| if ($Platforms.Count -eq 0) { | |
| throw "This build produced no Linux runtime ($env:SERVER_RIDS), so there is nothing to put in a container image." | |
| } | |
| Add-Content -Path $env:GITHUB_OUTPUT -Value "version=$env:RELEASE_VERSION" | |
| Add-Content -Path $env:GITHUB_OUTPUT -Value "platforms=$($Platforms -join ',')" | |
| Add-Content -Path $env:GITHUB_OUTPUT -Value "docker_tag=$DockerTag" | |
| Write-Host "Version: $env:RELEASE_VERSION" | |
| Write-Host "Platforms: $($Platforms -join ',')" | |
| Write-Host "Docker tag: $DockerTag" | |
| - name: Create Dockerfile | |
| shell: pwsh | |
| run: | | |
| @' | |
| FROM mcr.microsoft.com/dotnet/aspnet:10.0 | |
| RUN apt update | |
| RUN apt -y install curl | |
| USER $APP_UID | |
| WORKDIR /app | |
| EXPOSE 8080 | |
| EXPOSE 8081 | |
| # Each runtime is unpacked under its Docker architecture name. | |
| ARG TARGETARCH | |
| COPY ${TARGETARCH}/ /app | |
| ENTRYPOINT ["dotnet", "ControlR.Web.Server.dll"] | |
| HEALTHCHECK \ | |
| CMD curl -f http://localhost:8080/health || exit 1 | |
| '@ | Set-Content -Path ./payload/Dockerfile -Encoding utf8 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| # Harmless when the runner's own architecture is the only target. | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v4 | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKER_USERNAME }} | |
| password: ${{ secrets.DOCKER_PAT }} | |
| - name: Build and Push Image | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: ./payload | |
| file: ./payload/Dockerfile | |
| push: true | |
| platforms: ${{ steps.payload.outputs.platforms }} | |
| tags: | | |
| bitbound/controlr:${{ steps.payload.outputs.docker_tag }} | |
| bitbound/controlr:${{ steps.payload.outputs.version }} | |
| - name: Image Published | |
| shell: pwsh | |
| env: | |
| VERSION: ${{ steps.payload.outputs.version }} | |
| PLATFORMS: ${{ steps.payload.outputs.platforms }} | |
| DOCKER_TAG: ${{ steps.payload.outputs.docker_tag }} | |
| run: | | |
| Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "## Publish Summary" | |
| Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "**Version:** $env:VERSION" | |
| Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "**Platforms:** $env:PLATFORMS" | |
| Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "**Tags:**" | |
| Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "- bitbound/controlr:$env:DOCKER_TAG" | |
| Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "- bitbound/controlr:$env:VERSION" |