From b2c8594a7b11795e389bc10922879ced9bd3279d Mon Sep 17 00:00:00 2001 From: Mark Harrison Date: Thu, 11 Jul 2024 13:49:15 +0100 Subject: [PATCH] Add octo-cli-ci --- .../octo-cli-ci-docker-build-push.yml | 174 ++++++++++++++++++ .../workflows/octo-cli-ci-ghcr-build-push.yml | 161 ++++++++++++++++ README.md | 16 ++ octo-cli-ci/linux-amd64/dockerfile | 3 + 4 files changed, 354 insertions(+) create mode 100644 .github/workflows/octo-cli-ci-docker-build-push.yml create mode 100644 .github/workflows/octo-cli-ci-ghcr-build-push.yml create mode 100644 octo-cli-ci/linux-amd64/dockerfile diff --git a/.github/workflows/octo-cli-ci-docker-build-push.yml b/.github/workflows/octo-cli-ci-docker-build-push.yml new file mode 100644 index 0000000..5f9316d --- /dev/null +++ b/.github/workflows/octo-cli-ci-docker-build-push.yml @@ -0,0 +1,174 @@ +name: Build octo-cli-ci Docker image + +on: + push: + branches: + - main + paths: + - octo-cli/** +# No schedule as this cli is deprecated +# schedule: +# - cron: '0 2 * * *' + workflow_dispatch: + +env: + REGISTRY_IMAGE: octopuslabs/octo-cli-ci + +jobs: + + get-version-number: + runs-on: windows-latest + outputs: + CONTINUE: ${{ steps.check-version.outputs.CONTINUE }} + VERSION: ${{ steps.check-version.outputs.VERSION }} + steps: + - uses: actions/checkout@v4 + - id: check-version + name: Compare latest version with container + run: | + $chocoInformationRaw = choco info octopustools --limitoutput + $versionOutput = ($chocoInformationRaw.Split("|"))[1] + + [System.Version]$version = $null + $versionParsed = [System.Version]::TryParse($versionOutput, [ref]$version) + if(-not $versionParsed) { + Write-Host "Unable to parse '$versionOutput' as a valid version. Won't continue" + echo "CONTINUE=No" >> $env:GITHUB_OUTPUT + } + else { + $versionToCompare = "$($version.Major).$($version.Minor).$($version.Build)" + Write-Host "Parsed version as $versionToCompare" + + echo "VERSION=$versionToCompare" >> $env:GITHUB_OUTPUT + + Write-Host "Retrieving tags ..." + + $response = try { + $repositoryTags = Invoke-RestMethod "https://registry.hub.docker.com/v2/repositories/${{ env.REGISTRY_IMAGE }}/tags" + Write-Host "Retrieval successful!" + } catch [System.Net.WebException] { + $_.Exception.Response + Write-Host "Retrieval failed!!" + } + + if ($null -eq $response) + { + $matchingTag = $repositoryTags.results | Where-Object {$_.Name -eq $versionToCompare} + + if ($null -ne $matchingTag) + { + Write-Host "Docker container already has latest version." + echo "CONTINUE=No" >> $env:GITHUB_OUTPUT + } + else + { + Write-Host "vNext Octopus CLI has been updated, create new image." + echo "CONTINUE=Yes" >> $env:GITHUB_OUTPUT + } + } + else + { + if ($response.StatusCode.value__ -eq 404) + { + Write-Host "No tags exist for repo, assuming first build." + echo "CONTINUE=Yes" >> $env:GITHUB_OUTPUT + } + } + } + shell: powershell + + build-linux: + needs: [get-version-number] + if: ${{ needs.get-version-number.outputs.CONTINUE == 'Yes' }} + strategy: + matrix: + os: + - ubuntu-latest + platform: + - linux/amd64 + runs-on: ${{ matrix.os }} + env: + VERSION: ${{ needs.get-version-number.outputs.VERSION }} + steps: + + - name: Prepare + run: | + platform=${{ matrix.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + echo "PLATFORM_ARCH=${platform//[linux\/]/}" >> $GITHUB_ENV + + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: linux/amd64,linux/arm64 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USER }} + password: ${{ secrets.DOCKER_HUB_PAT }} + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v5 + with: + context: octo-cli/${{ env.PLATFORM_PAIR }} + platforms: ${{ matrix.platform }} + provenance: false + outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + run: | + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ env.PLATFORM_PAIR }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + merge: + needs: [get-version-number, build-linux] + if: ${{ needs.get-version-number.outputs.CONTINUE == 'Yes' }} + runs-on: ubuntu-latest + env: + VERSION_NUMBER: ${{ needs.get-version-number.outputs.VERSION }} + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: /tmp/digests + pattern: digests-* + merge-multiple: true + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY_IMAGE }} + tags: | + ${{ env.VERSION_NUMBER }} + latest + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USER }} + password: ${{ secrets.DOCKER_HUB_PAT }} + - name: Create manifest list and push + working-directory: /tmp/digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }} \ No newline at end of file diff --git a/.github/workflows/octo-cli-ci-ghcr-build-push.yml b/.github/workflows/octo-cli-ci-ghcr-build-push.yml new file mode 100644 index 0000000..a1bd36f --- /dev/null +++ b/.github/workflows/octo-cli-ci-ghcr-build-push.yml @@ -0,0 +1,161 @@ +name: Build GHCR octo-cli-ci image + +permissions: + packages: write +on: + push: + branches: + - main + paths: + - octo-cli-ci/** + # No schedule as this cli is deprecated + # schedule: + # - cron: '0 2 * * *' + workflow_dispatch: + +env: + REGISTRY_IMAGE: ghcr.io/octopusdeploylabs/octo-cli-ci + +jobs: + + get-version-number: + runs-on: windows-latest + outputs: + CONTINUE: ${{ steps.check-version.outputs.CONTINUE }} + VERSION: ${{ steps.check-version.outputs.VERSION }} + steps: + - uses: actions/checkout@v4 + - id: check-version + name: Compare latest version with container + run: | + $chocoInformationRaw = choco info octopustools --limitoutput + $versionOutput = ($chocoInformationRaw.Split("|"))[1] + + [System.Version]$version = $null + $versionParsed = [System.Version]::TryParse($versionOutput, [ref]$version) + if(-not $versionParsed) { + Write-Host "Unable to parse '$versionOutput' as a valid version. Won't continue" + echo "CONTINUE=No" >> $env:GITHUB_OUTPUT + } + else { + $versionToCompare = "$($version.Major).$($version.Minor).$($version.Build)" + Write-Host "Parsed version as $versionToCompare" + + $token = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("${{ secrets.GITHUB_TOKEN }}")) + $workerToolsTags = Invoke-RestMethod -Uri "https://ghcr.io/v2/${{ env.REGISTRY_IMAGE }}/tags/list" -Headers @{Authorization="Bearer $token"} -SkipHttpErrorCheck + $matchingTag = $workerToolsTags.tags | Where-Object { $_ -eq $versionToCompare } + + echo "VERSION=$versionToCompare" >> $env:GITHUB_OUTPUT + + if ($null -ne $matchingTag -or ($null -ne $workerToolsTags.errors -and $workerToolsTags.errors[0].code -ne "NAME_UNKNOWN")) + { + Write-Host "Docker container already has latest version" + echo "CONTINUE=No" >> $env:GITHUB_OUTPUT + } + else + { + Write-Host "We need to upgrade the container to $versionToCompare" + + Write-Host "We have everything we need, continuing." + echo "CONTINUE=Yes" >> $env:GITHUB_OUTPUT + } + } + shell: pwsh + + build-linux: + needs: [get-version-number] + if: ${{ needs.get-version-number.outputs.CONTINUE == 'Yes' }} + strategy: + matrix: + os: + - ubuntu-latest + platform: + - linux/amd64 + runs-on: ${{ matrix.os }} + env: + VERSION: ${{ needs.get-version-number.outputs.VERSION }} + steps: + + - name: Prepare + run: | + platform=${{ matrix.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + echo "PLATFORM_ARCH=${platform//[linux\/]/}" >> $GITHUB_ENV + + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: linux/amd64,linux/arm64 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: https://ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v5 + with: + context: octo-cli-ci/${{ env.PLATFORM_PAIR }} + platforms: ${{ matrix.platform }} + provenance: false + outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + run: | + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ env.PLATFORM_PAIR }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + merge: + needs: [get-version-number, build-linux] + if: ${{ needs.get-version-number.outputs.CONTINUE == 'Yes' }} + runs-on: ubuntu-latest + env: + VERSION_NUMBER: ${{ needs.get-version-number.outputs.VERSION }} + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: /tmp/digests + pattern: digests-* + merge-multiple: true + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY_IMAGE }} + tags: | + ${{ env.VERSION_NUMBER }} + latest + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: https://ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Create manifest list and push + working-directory: /tmp/digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }} \ No newline at end of file diff --git a/README.md b/README.md index d37792c..e5eb6bf 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,13 @@ The following DockerHub images are built from this repository: - [octopus-cli](#octopus-cli) (`octopuslabs/octopus-cli`) - [octopus-cli-ci](#octopus-cli-ci) (`octopuslabs/octopus-cli-ci`) +- [octo-cli-ci](#octo-cli-ci) (`octopuslabs/octo-cli-ci`) We also publish the same images to GitHub's Container Registry (GHRC) too: - `ghcr.io/octopusdeploylabs/octopus-cli` - `ghcr.io/octopusdeploylabs/octopus-cli-ci` +- `ghcr.io/octopusdeploylabs/octo-cli-ci` ### Octopus CLI @@ -57,6 +59,20 @@ A new image is built each time a new version of the `octopus` CLI is detected. T You can retrieve a list of all available tags on [DockerHub](https://hub.docker.com/r/octopuslabs/octopus-cli-ci/tags). +### Octo CLI CI + +This is a CI-server compatible image containing the (now deprecated) `octo` [command line tool](https://github.com/OctopusDeploy/OctopusCli/blob/main/README.md). +It's identical to the [octopusdeploy/octo](https://hub.docker.com/r/octopusdeploy/octo) image except that its `ENTRYPOINT` method is overriden to be empty (`""`). + +The version tag corresponds to the version of the `octo` CLI on the image. + +#### Tags + +- `octopuslabs/octo-cli-ci:latest` +- `octopuslabs/octo-cli-ci:VERSION` + +You can retrieve a list of all available tags on [DockerHub](https://hub.docker.com/r/octopuslabs/octopus-cli-ci/tags). + ## Support If you find a bug or encounter a problem with these images, please raise an [issue](https://github.com/OctopusDeployLabs/octopus-cli-docker/issues). diff --git a/octo-cli-ci/linux-amd64/dockerfile b/octo-cli-ci/linux-amd64/dockerfile new file mode 100644 index 0000000..14cecef --- /dev/null +++ b/octo-cli-ci/linux-amd64/dockerfile @@ -0,0 +1,3 @@ +FROM octopusdeploy/octo:latest + +ENTRYPOINT [""] \ No newline at end of file