Add feature to push manifest (#55)

* Add feature to push manifest

Signed-off-by: divyansh42 <diagrawa@redhat.com>
This commit is contained in:
Divyanshu Agrawal 2021-11-17 17:30:09 +05:30 committed by divyansh42
parent 3220bde582
commit 56f05cb637
7 changed files with 209 additions and 78 deletions

View file

@ -0,0 +1,75 @@
# This workflow will perform a test whenever there
# is some change in code done to ensure that the changes
# are not buggy and we are getting the desired output.
name: Build and Push Manifest
on:
push:
workflow_dispatch:
schedule:
- cron: '0 0 * * *' # every day at midnight
env:
IMAGE_NAME: ptr-manifest
IMAGE_TAGS: v1 ${{ github.sha }}
IMAGE_REGISTRY: quay.io
IMAGE_NAMESPACE: redhat-github-actions
jobs:
push-quay:
name: Build and push manifest
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
install_latest: [ true, false ]
steps:
# Checkout push-to-registry action github repository
- name: Checkout Push to Registry action
uses: actions/checkout@v2
- name: Install latest podman
if: matrix.install_latest
run: |
bash .github/install_latest_podman.sh
- name: Install qemu dependency
run: |
sudo apt-get update
sudo apt-get install -y qemu-user-static
- name: Create Containerfile
run: |
cat > Containerfile<<EOF
FROM docker.io/alpine:3.14
RUN echo "hello world"
ENTRYPOINT [ "sh", "-c", "echo -n 'Machine: ' && uname -m && echo -n 'Bits: ' && getconf LONG_BIT && echo 'goodbye world'" ]
EOF
- name: Build Image
id: build_image
uses: redhat-actions/buildah-build@main
with:
image: ${{ env.IMAGE_NAME }}
tags: ${{ env.IMAGE_TAGS }}
archs: amd64, arm64
containerfiles: |
./Containerfile
# Push the image manifest to Quay.io (Image Registry)
- name: Push To Quay
uses: ./
id: push
with:
image: ${{ steps.build_image.outputs.image }}
tags: ${{ steps.build_image.outputs.tags }}
registry: ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAMESPACE }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Echo outputs
run: |
echo "${{ toJSON(steps.push.outputs) }}"

View file

@ -12,7 +12,7 @@
[![license badge](https://img.shields.io/github/license/redhat-actions/push-to-registry)](./LICENSE)
[![size badge](https://img.shields.io/github/size/redhat-actions/push-to-registry/dist/index.js)](./dist)
Push-to-registry is a GitHub Action for pushing a container image to an image registry, such as Dockerhub, quay&#46;io, the GitHub Container Registry, or an OpenShift integrated registry.
Push-to-registry is a GitHub Action for pushing a container image or an [image manifest](https://github.com/containers/buildah/blob/main/docs/buildah-manifest.1.md) to an image registry, such as Dockerhub, quay&#46;io, the GitHub Container Registry, or an OpenShift integrated registry.
This action only runs on Linux, as it uses [podman](https://github.com/containers/Podman) to perform the push. [GitHub's Ubuntu action runners](https://github.com/actions/virtual-environments#available-environments) come with Podman preinstalled. If you are not using those runners, you must first [install Podman](https://podman.io/getting-started/installation).
@ -24,8 +24,8 @@ Refer to the [`podman push`](http://docs.podman.io/en/latest/markdown/podman-man
| Input Name | Description | Default |
| ---------- | ----------- | ------- |
| image | Name of the image you want to push. Eg. `username/imagename` or `imagename`. Refer to [Image and Tag Inputs](https://github.com/redhat-actions/push-to-registry#image-tag-inputs). | **Required** - unless all tags include registry and image name
| tags | The tag or tags of the image to push. For multiple tags, separate by whitespace. Refer to [Image and Tag Inputs](https://github.com/redhat-actions/push-to-registry#image-tag-inputs). | `latest`
| image | Name of the image or manifest you want to push. Eg. `username/imagename` or `imagename`. Refer to [Image and Tag Inputs](https://github.com/redhat-actions/push-to-registry#image-tag-inputs). | **Required** - unless all tags include registry and image name
| tags | The tag or tags of the image or manifest to push. For multiple tags, separate by whitespace. Refer to [Image and Tag Inputs](https://github.com/redhat-actions/push-to-registry#image-tag-inputs). | `latest`
| registry | Hostname and optional namespace to push the image to. Eg. `quay.io` or `quay.io/username`. Refer to [Image and Tag Inputs](https://github.com/redhat-actions/push-to-registry#image-tag-inputs). | **Required** - unless all tags include registry and image name
| username | Username with which to authenticate to the registry. Required unless already logged in to the registry. | None
| password | Password, encrypted password, or access token to use to log in to the registry. Required unless already logged in to the registry. | None
@ -83,6 +83,12 @@ For example:
`registry-path`: The first element of `registry-paths`, as a string.
## Pushing Manifest
If multiple tags are provided, either all tags must point to manifests, or none of them. i.e., you cannot push both manifests are regular images in one `push-to-registry` step.
Refer to [Manifest Build and Push example](./.github/workflows/manifest-build-push.yaml) for a sophisticated example of building and pushing a manifest.
## Examples
The example below shows how the `push-to-registry` action can be used to push an image created by the [**buildah-build**](https://github.com/redhat-actions/buildah-build) action.

View file

@ -6,10 +6,12 @@ branding:
color: red
inputs:
image:
description: 'Name of the image to push (e.g. username/imagename or imagename)'
description: 'Name of the image/manifest to push (e.g. username/imagename or imagename)'
required: false
tags:
description: 'The tag or tags of the image to push. For multiple tags, seperate by whitespace. For example, "latest v1"'
description: |
'The tag or tags of the image/manifest to push.
For multiple tags, seperate by whitespace. For example, "latest v1"'
required: false
default: 'latest'
registry:
@ -39,7 +41,7 @@ inputs:
outputs:
digest:
description: 'The pushed image digest, as written to the "digestfile"'
description: 'The pushed image/manifest digest, as written to the "digestfile"'
registry-path:
description: 'The first element of registry-paths.'
registry-paths:

2
dist/index.js vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View file

@ -16,7 +16,7 @@ export enum Inputs {
*/
EXTRA_ARGS = "extra-args",
/**
* Name of the image to push (e.g. username/imagename or imagename)
* Name of the image/manifest to push (e.g. username/imagename or imagename)
* Required: false
* Default: None.
*/
@ -34,7 +34,8 @@ export enum Inputs {
*/
REGISTRY = "registry",
/**
* The tag or tags of the image to push. For multiple tags, seperate by whitespace. For example, "latest v1"
* 'The tag or tags of the image/manifest to push.
* For multiple tags, seperate by whitespace. For example, "latest v1"'
* Required: false
* Default: "latest"
*/
@ -55,7 +56,7 @@ export enum Inputs {
export enum Outputs {
/**
* The pushed image digest, as written to the "digestfile"
* The pushed image/manifest digest, as written to the "digestfile"
* Required: false
* Default: None.
*/

View file

@ -107,7 +107,11 @@ async function run(): Promise<void> {
}
const registryPathList: string[] = [];
// here
// check if provided image is manifest or not
const isManifest = await checkIfManifestsExists();
if (!isManifest) {
// check if image with all the required tags exist in Podman image storage
const podmanImageStorageCheckResult: ImageStorageCheckResult = await checkImageInPodman();
@ -188,6 +192,7 @@ async function run(): Promise<void> {
+ `image storage. The image(s) will be pushed from Podman image storage.`
);
}
}
let pushMsg = `⏳ Pushing "${sourceImages.join(", ")}" to "${destinationImages.join(", ")}" respectively`;
if (username) {
@ -216,16 +221,25 @@ async function run(): Promise<void> {
// push the image
for (let i = 0; i < destinationImages.length; i++) {
const args = [
...(isImageFromDocker ? dockerPodmanOpts : []),
const args = [];
if (isImageFromDocker) {
args.push(...dockerPodmanOpts);
}
if (isManifest) {
args.push("manifest");
}
args.push(...[
"push",
"--quiet",
"--digestfile",
digestFile,
isImageFromDocker ? getFullDockerImageName(sourceImages[i]) : sourceImages[i],
destinationImages[i],
];
]);
// to push all the images referenced in the manifest
if (isManifest) {
args.push("--all");
}
if (podmanExtraArgs.length > 0) {
args.push(...podmanExtraArgs);
}
@ -392,6 +406,39 @@ async function removeDockerPodmanImageStroage(): Promise<void> {
}
}
async function checkIfManifestsExists(): Promise<boolean> {
const foundManifests = [];
const missingManifests = [];
// check if manifest exist in Podman's storage
core.info(`🔍 Checking if the given image is manifest or not.`);
for (const manifest of sourceImages) {
const commandResult: ExecResult = await execute(
await getPodmanPath(),
[ "manifest", "exists", manifest ],
{ ignoreReturnCode: true, group: true }
);
if (commandResult.exitCode === 0) {
foundManifests.push(manifest);
}
else {
missingManifests.push(manifest);
}
}
if (foundManifests.length > 0) {
core.info(`Image${foundManifests.length !== 1 ? "s" : ""} "${foundManifests.join(", ")}" `
+ `${foundManifests.length !== 1 ? "are manifests" : "is a manifest"}.`);
}
if (foundManifests.length > 0 && missingManifests.length > 0) {
throw new Error(`Manifest${missingManifests.length !== 1 ? "s" : ""} "${missingManifests.join(", ")}" `
+ `not found in the Podman image storage. Make sure that all the provided images are either `
+ `manifests or container images.`);
}
return foundManifests.length === sourceImages.length;
}
async function execute(
executable: string,
args: string[],