Compare commits

..

No commits in common. "main" and "v2.3" have entirely different histories.
main ... v2.3

21 changed files with 5636 additions and 3219 deletions

View file

@ -1,3 +1,7 @@
# https://podman.io/getting-started/installation
. /etc/os-release
echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
curl -sSfL "https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/Release.key" | sudo apt-key add -
sudo apt-get update
sudo apt-get -y upgrade
sudo apt-get -y install podman

View file

@ -1,65 +0,0 @@
# 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: Check Case Normalization
on:
push:
workflow_dispatch:
schedule:
- cron: '0 0 * * *' # every day at midnight
env:
IMAGE_NAME: ImageCaseTest
IMAGE_TAGS: v1 TagCaseTest ${{ github.sha }}
IMAGE_REGISTRY: Ghcr.io/${{ github.repository_owner }}
REGISTRY_USER: ${{ github.actor }}
REGISTRY_PASSWORD: ${{ github.token }}
jobs:
push-ghcr:
name: Build and push image
runs-on: ubuntu-22.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@v4
- name: Install latest podman
if: matrix.install_latest
run: |
bash .github/install_latest_podman.sh
# Build image using Buildah action
- name: Build Image
id: build_image
uses: redhat-actions/buildah-build@v2
with:
image: ${{ env.IMAGE_NAME }}
tags: ${{ env.IMAGE_TAGS }}
base-image: busybox:latest
entrypoint: |
bash
-c
echo 'hello world'
oci: true
# Push the image to GHCR (Image Registry)
- name: Push To GHCR
uses: ./
id: push
with:
image: ${{ steps.build_image.outputs.image }}
tags: ${{ steps.build_image.outputs.tags }}
registry: ${{ env.IMAGE_REGISTRY }}
username: ${{ env.REGISTRY_USER }}
password: ${{ env.REGISTRY_PASSWORD }}
extra-args: |
--disable-content-trust
- name: Echo outputs
run: |
echo "${{ toJSON(steps.push.outputs) }}"

View file

@ -9,18 +9,18 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- run: npm ci
- run: npm run lint
check-dist:
name: Check Distribution
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
env:
BUNDLE_FILE: "dist/index.js"
BUNDLE_COMMAND: "npm run bundle"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Install
run: npm ci
@ -33,11 +33,11 @@ jobs:
check-inputs-outputs:
name: Check Input and Output enums
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
env:
IO_FILE: ./src/generated/inputs-outputs.ts
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Install dependencies
run: npm ci

View file

@ -1,65 +0,0 @@
# 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: Push to GHCR
on:
push:
workflow_dispatch:
schedule:
- cron: '0 0 * * *' # every day at midnight
env:
IMAGE_NAME: ptr-test
IMAGE_TAGS: v1 ${{ github.sha }}
IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }}
REGISTRY_USER: ${{ github.actor }}
REGISTRY_PASSWORD: ${{ github.token }}
jobs:
push-ghcr:
name: Build and push image
runs-on: ubuntu-22.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@v4
- name: Install latest podman
if: matrix.install_latest
run: |
bash .github/install_latest_podman.sh
# Build image using Buildah action
- name: Build Image
id: build_image
uses: redhat-actions/buildah-build@v2
with:
image: ${{ env.IMAGE_NAME }}
tags: ${{ env.IMAGE_TAGS }}
base-image: busybox:latest
entrypoint: |
bash
-c
echo 'hello world'
oci: true
# Push the image to GHCR (Image Registry)
- name: Push To GHCR
uses: ./
id: push
with:
image: ${{ steps.build_image.outputs.image }}
tags: ${{ steps.build_image.outputs.tags }}
registry: ${{ env.IMAGE_REGISTRY }}
username: ${{ env.REGISTRY_USER }}
password: ${{ env.REGISTRY_PASSWORD }}
extra-args: |
--disable-content-trust
- name: Echo outputs
run: |
echo "${{ toJSON(steps.push.outputs) }}"

View file

@ -12,9 +12,9 @@ on:
jobs:
markdown-link-check:
name: Check links in markdown
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- uses: gaurav-nelson/github-action-markdown-link-check@v1
with:
use-verbose-mode: true

View file

@ -1,75 +0,0 @@
# 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-22.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@v4
- 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@v2
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

@ -10,52 +10,30 @@ env:
IMAGE_NAMESPACE: redhat-github-actions
IMAGE_NAME: ptr-test
IMAGE_TAG: v1
SHORT_IMAGE_NAME_TAG: ptr-test:v1
FULLY_QUALIFIED_IMAGE_NAME_TAG: quay.io/redhat-github-actions/ptr-test:v1
jobs:
build:
name: |-
Build with ${{ matrix.build_with }} and push${{ matrix.fully_qualified_image_name_tag && ' FQIN' || '' }} (latest: ${{ matrix.install_latest }})
runs-on: ubuntu-22.04
build-only-podman:
name: Build and push image built only on Podman
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
install_latest: [ true, false ]
build_with: [ "docker after podman", "podman after docker", "podman only", "docker only" ]
fully_qualified_image_name_tag: [ true, false ]
steps:
# Checkout push-to-registry action github repository
- name: Checkout Push to Registry action
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: Install latest podman
if: matrix.install_latest
run: |
bash .github/install_latest_podman.sh
- name: Build image using Docker
if: endsWith(matrix.build_with, 'docker')
run: |
docker build -t ${{ matrix.fully_qualified_image_name_tag && env.FULLY_QUALIFIED_IMAGE_NAME_TAG || env.SHORT_IMAGE_NAME_TAG }} -<<EOF
FROM busybox
RUN echo "hello world"
EOF
- name: Build image using Podman
if: contains(matrix.build_with, 'podman')
run: |
podman build -t ${{ matrix.fully_qualified_image_name_tag && env.FULLY_QUALIFIED_IMAGE_NAME_TAG || env.SHORT_IMAGE_NAME_TAG }} -<<EOF
FROM busybox
RUN echo "hello world"
EOF
- name: Build image using Docker
if: startsWith(matrix.build_with, 'docker')
run: |
docker build -t ${{ matrix.fully_qualified_image_name_tag && env.FULLY_QUALIFIED_IMAGE_NAME_TAG || env.SHORT_IMAGE_NAME_TAG }} -<<EOF
podman build -t ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} -<<EOF
FROM busybox
RUN echo "hello world"
EOF
@ -65,7 +43,141 @@ jobs:
uses: ./
with:
image: ${{ env.IMAGE_NAME }}
tags: ${{ matrix.fully_qualified_image_name_tag && env.FULLY_QUALIFIED_IMAGE_NAME_TAG || env.IMAGE_TAG }}
tags: ${{ env.IMAGE_TAG }}
registry: ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAMESPACE }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Echo outputs
run: |
echo "${{ toJSON(steps.push.outputs) }}"
build-only-docker:
name: Build and push image built only on Docker
runs-on: ubuntu-18.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: Build image using Docker
run: |
docker build -t ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} -<<EOF
FROM busybox
RUN echo "hello world"
EOF
- name: Push image to ${{ env.IMAGE_REGISTRY }}
id: push
uses: ./
with:
image: ${{ env.IMAGE_NAME }}
tags: ${{ env.IMAGE_TAG }}
registry: ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAMESPACE }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Echo outputs
run: |
echo "${{ toJSON(steps.push.outputs) }}"
build-podman-latest:
name: Build and push image built latest on Podman
runs-on: ubuntu-18.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: Build image using Docker
run: |
docker build -t ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} -<<EOF
FROM busybox
RUN echo "hello world"
EOF
- name: Build image using Podman
run: |
podman build -t ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} -<<EOF
FROM busybox
RUN echo "hello world"
EOF
- name: Push image to ${{ env.IMAGE_REGISTRY }}
id: push
uses: ./
with:
image: ${{ env.IMAGE_NAME }}
tags: ${{ env.IMAGE_TAG }}
registry: ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAMESPACE }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Echo outputs
run: |
echo "${{ toJSON(steps.push.outputs) }}"
build-docker-latest:
name: Build and push image built latest on Docker
runs-on: ubuntu-18.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: Build image using Podman
run: |
podman build -t ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} -<<EOF
FROM busybox
RUN echo "hello world"
EOF
- name: Build image using Docker
run: |
docker build -t ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} -<<EOF
FROM busybox
RUN echo "hello world"
EOF
- name: Push image to ${{ env.IMAGE_REGISTRY }}
id: push
uses: ./
with:
image: ${{ env.IMAGE_NAME }}
tags: ${{ env.IMAGE_TAG }}
registry: ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAMESPACE }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}

View file

@ -1,35 +0,0 @@
name: Vulnerability Scan with CRDA
on:
# push:
workflow_dispatch:
# pull_request_target:
# types: [ assigned, opened, synchronize, reopened, labeled, edited ]
# schedule:
# - cron: '0 0 * * *' # every day at midnight
jobs:
crda-scan:
runs-on: ubuntu-22.04
name: Scan project vulnerability with CRDA
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install CRDA
uses: redhat-actions/openshift-tools-installer@v1
with:
source: github
github_pat: ${{ github.token }}
crda: "latest"
- name: CRDA Scan
id: scan
uses: redhat-actions/crda@v1
with:
crda_key: ${{ secrets.CRDA_KEY }}
fail_on: never

View file

@ -17,7 +17,7 @@ env:
jobs:
login-and-push:
name: Login and push image to Quay.io
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
@ -27,7 +27,7 @@ jobs:
# Checkout push-to-registry action github repository
- name: Checkout Push to Registry action
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: Install latest podman
if: matrix.install_latest

View file

@ -1,7 +1,7 @@
# 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: Push to Quay.io
name: Build and Push
on:
push:
workflow_dispatch:
@ -9,15 +9,16 @@ on:
- cron: '0 0 * * *' # every day at midnight
env:
IMAGE_NAME: ptr-test
IMAGE_TAGS: v1 ${{ github.sha }}
IMAGE_REGISTRY: quay.io
IMAGE_NAMESPACE: redhat-github-actions
IMAGE_NAME: ptr-test
IMAGE_TAGS: v1 ${{ github.sha }}
MVN_REPO_DIR: ~/.m2/repository
jobs:
push-quay:
name: Build and push image
runs-on: ubuntu-22.04
build-and-push:
name: Build and push image to Quay.io
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
@ -26,7 +27,7 @@ jobs:
steps:
# Checkout push-to-registry action github repository
- name: Checkout Push to Registry action
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: Install latest podman
if: matrix.install_latest

View file

@ -1,31 +1,5 @@
# push-to-registry Changelog
## v2.8
- Update action to run on Node20. https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/
## v2.7.1
- Don't add docker.io prefix to ECR images. [#69](https://github.com/redhat-actions/push-to-registry/pull/69)
## v2.7
- Update action to run on Node16. https://github.blog/changelog/2022-05-20-actions-can-now-run-in-a-node-js-16-runtime/
## v2.6
- Make image and tag in lowercase, if found in uppercase. https://github.com/redhat-actions/push-to-registry/issues/54
- Remove kubic packages from the test workflows. https://github.com/redhat-actions/buildah-build/issues/93
## v2.5.1
- README update
## v2.5
- Allow pushing image manifest.
## v2.4.1
- Fix issue when pushing multiple tags. [#57](https://github.com/redhat-actions/push-to-registry/issues/57)
## v2.4
- Allow fully qualified image names in `tags` input, for compatibility with [docker/metadata-action`](https://github.com/docker/metadata-action). [#50](https://github.com/redhat-actions/push-to-registry/pull/50)
- Fix issue where image pulled from Docker storage would overwrite image in Podman storage [733d8e9](https://github.com/redhat-actions/buildah-build/commit/733d8e9a389084e2f8c441f0a568e5d467497557)
## v2.3.2
- Add the word `local` to the image check messages.
- Add matrix to install latest podman. (Internal)

View file

@ -1,19 +1,16 @@
# push-to-registry
[![CI checks](https://github.com/redhat-actions/push-to-registry/workflows/CI%20checks/badge.svg)](https://github.com/redhat-actions/push-to-registry/actions?query=workflow%3A%22CI+checks%22)
[![Link checker](https://github.com/redhat-actions/push-to-registry/workflows/Link%20checker/badge.svg)](https://github.com/redhat-actions/push-to-registry/actions?query=workflow%3A%22Link+checker%22)
<br><br>
[![Push to Quay.io](https://github.com/redhat-actions/push-to-registry/actions/workflows/quay-push.yaml/badge.svg)](https://github.com/redhat-actions/push-to-registry/actions/workflows/quay-push.yaml)
[![Push to GHCR](https://github.com/redhat-actions/push-to-registry/actions/workflows/ghcr-push.yaml/badge.svg)](https://github.com/redhat-actions/push-to-registry/actions/workflows/ghcr-push.yaml)
[![Build and Push](https://github.com/redhat-actions/push-to-registry/workflows/Build%20and%20Push/badge.svg)](https://github.com/redhat-actions/push-to-registry/actions?query=workflow%3A%22Build+and+Push%22)
[![Login and Push](https://github.com/redhat-actions/push-to-registry/workflows/Login%20and%20Push/badge.svg)](https://github.com/redhat-actions/push-to-registry/actions?query=workflow%3A%22Login+and+Push%22)
[![Build and Push Manifest](https://github.com/redhat-actions/push-to-registry/actions/workflows/manifest-build-push.yaml/badge.svg)](https://github.com/redhat-actions/push-to-registry/actions/workflows/manifest-build-push.yaml)
[![Multiple container CLI build tests](https://github.com/redhat-actions/push-to-registry/workflows/Multiple%20container%20CLI%20build%20tests/badge.svg)](https://github.com/redhat-actions/push-to-registry/actions?query=workflow%3A%22Multiple+container+CLI+build+tests%22)
[![Link checker](https://github.com/redhat-actions/push-to-registry/workflows/Link%20checker/badge.svg)](https://github.com/redhat-actions/push-to-registry/actions?query=workflow%3A%22Link+checker%22)
<br><br>
[![tag badge](https://img.shields.io/github/v/tag/redhat-actions/push-to-registry)](https://github.com/redhat-actions/push-to-registry/tags)
[![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 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.
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.
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).
@ -25,44 +22,16 @@ Refer to the [`podman push`](http://docs.podman.io/en/latest/markdown/podman-man
| Input Name | Description | Default |
| ---------- | ----------- | ------- |
| 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
| image | Name of the image you want to push. Eg. `username/imagename` or `imagename`. See the note below about naming image and registry. | **Required**
| tags | The tag or tags of the image to push. For multiple tags, separate by a space. For example, `latest ${{ github.sha }}`. | `latest`
| registry | Hostname and optional namespace to push the image to. Eg. `quay.io` or `quay.io/username`. See the note below about naming image and registry. | **Required**
| 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
| tls-verify | Verify TLS certificates when contacting the registry. Set to `false` to skip certificate verification. | `true`
| digestfile | After copying the image, write the digest of the resulting image to the file. The contents of this file are the digest output. | Auto-generated from image and tag
| extra-args | Extra args to be passed to podman push. Separate arguments by newline. Do not use quotes. | None
<a id="image-tag-inputs"></a>
### Image, Tag and Registry Inputs
The **push-to-registry** `image` and `tag` input work very similarly to [**buildah-build**](https://github.com/redhat-actions/buildah-build#image-tag-inputs).
However, when using **push-to-registry** when the `tags` input are not fully qualified, the `registry` input must also be set.
So, for **push-to-registry** the options are as follows:
**Option 1**: Provide `registry`, `image`, and `tags` inputs. The image(s) will be pushed to `${registry}/${image}:${tag}`.
For example:
```yaml
registry: quay.io/my-namespace
image: my-image
tags: v1 v1.0.0
```
will push the image tags: `quay.io/my-namespace/my-image:v1` and `quay.io/my-namespace/my-image:v1.0.0`.
**Option 2**: Provide only the `tags` input, including the fully qualified image name in each tag. In this case, the `registry` and `image` inputs are ignored.
For example:
```yaml
# 'registry' and 'image' inputs are not set
tags: quay.io/my-namespace/my-image:v1 quay.io/my-namespace/my-image:v1.0.0
```
will push the image tags: `quay.io/my-namespace/my-image:v1` and `quay.io/my-namespace/my-image:v1.0.0`.
If the `tags` input does not have image names in the `${registry}/${name}:${tag}` form, then the `registry` and `image` inputs must be set.
**NOTE**: You can provide the registry namespace (usually your username, or organization) either as a suffix to input `registry` (eg. `quay.io/username`) or as a prefix to input `image` (eg. `username/myimage`), but not in both. The full image path will be resolved from `<registry>/<image>`.
## Action Outputs
@ -84,12 +53,6 @@ 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.
@ -101,10 +64,10 @@ on: [ push ]
jobs:
build:
name: Build and push image
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Build Image
id: build-image
@ -112,8 +75,8 @@ jobs:
with:
image: my-app
tags: latest ${{ github.sha }}
containerfiles: |
./Containerfile
dockerfiles: |
./Dockerfile
# Podman Login action (https://github.com/redhat-actions/podman-login) also be used to log in,
# in which case 'username' and 'password' can be omitted.
@ -130,8 +93,6 @@ jobs:
- name: Print image url
run: echo "Image pushed to ${{ steps.push-to-quay.outputs.registry-paths }}"
```
<!-- markdown-link-check-disable-next-line -->
Refer to [GHCR push example](./.github/workflows/ghcr-push.yaml) for complete example of push to [GitHub Container Registry (GHCR)](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry).
## Note about images built with Docker
@ -144,13 +105,11 @@ If the image to push is present in both the Docker and Podman image storage, the
If the action pulled an image from the Docker image storage into the Podman storage, it will be cleaned up from the Podman storage before the action exits.
## Note about GitHub runners and Podman
We recommend using `runs-on: ubuntu-22.04` since it has a newer version of Podman.
We recommend using `runs-on: ubuntu-20.04` since it has a newer version of Podman.
If you are on `ubuntu-20.04` or any other older versions of ubuntu your workflow will use an older version of Podman and may encounter issues such as [#26](https://github.com/redhat-actions/push-to-registry/issues/26).
If you are on `ubuntu-18.04` (which is currently aliased to [`ubuntu-latest`](https://github.com/actions/virtual-environments/issues/1816)) your workflow will use an older version of Podman and may encounter issues such as [#26](https://github.com/redhat-actions/push-to-registry/issues/26).
## Troubleshooting
Note that quay.io repositories are private by default.<br>
This means that if you push an image for the first time, you will have to authenticate before pulling it, or go to the repository's settings and change its visibility.
Similarly, if you receive a 403 Forbidden from GHCR, you may have to update the Package Settings. Refer to [this issue](https://github.com/redhat-actions/push-to-registry/issues/52).

View file

@ -6,17 +6,15 @@ branding:
color: red
inputs:
image:
description: 'Name of the image/manifest to push (e.g. username/imagename or imagename)'
required: false
description: 'Name of the image to push (e.g. username/imagename or imagename)'
required: true
tags:
description: |
'The tag or tags of the image/manifest to push.
For multiple tags, separate by whitespace. For example, "latest v1"'
description: 'The tag or tags of the image to push. For multiple tags, seperate by a space. For example, "latest v1"'
required: false
default: 'latest'
registry:
description: 'Hostname and optional namespace to push the image to (eg. quay.io/username or quay.io)'
required: false
required: true
username:
description: 'Username to use as credential to authenticate to the registry'
required: false
@ -41,11 +39,11 @@ inputs:
outputs:
digest:
description: 'The pushed image/manifest digest, as written to the "digestfile"'
description: 'The pushed image digest, as written to the "digestfile"'
registry-path:
description: 'The first element of registry-paths.'
registry-paths:
description: 'A JSON array of registry paths to which the tag(s) were pushed'
runs:
using: 'node20'
using: 'node12'
main: 'dist/index.js'

4
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

File diff suppressed because one or more lines are too long

3872
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "push-to-registry",
"version": "2.0.0",
"version": "0.0.1",
"description": "Action to push images to registry",
"main": "index.js",
"scripts": {
@ -8,27 +8,24 @@
"bundle": "ncc build src/index.ts --source-map --minify",
"clean": "rm -rf out/ dist/",
"lint": "eslint . --max-warnings=0",
"generate-ios": "npx action-io-generator -w -o ./src/generated/inputs-outputs.ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Red Hat",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/exec": "^1.1.1",
"@actions/io": "^1.1.3",
"ini": "^4.1.2"
"@actions/core": "^1.2.6",
"@actions/exec": "^1.0.4",
"@actions/io": "^1.0.2"
},
"devDependencies": {
"@redhat-actions/action-io-generator": "^1.5.0",
"@redhat-actions/eslint-config": "^1.3.2",
"@redhat-actions/tsconfig": "^1.2.0",
"@types/ini": "^4.1.0",
"@types/node": "^20.11.24",
"@typescript-eslint/eslint-plugin": "^7.1.1",
"@typescript-eslint/parser": "^7.1.1",
"@vercel/ncc": "^0.38.1",
"eslint": "^8.57.0",
"typescript": "5.3"
"@redhat-actions/tsconfig": "^1.1.1",
"@types/node": "^12.12.7",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"@vercel/ncc": "^0.25.1",
"eslint": "^7.18.0",
"typescript": "^4.0.5"
}
}

View file

@ -16,8 +16,8 @@ export enum Inputs {
*/
EXTRA_ARGS = "extra-args",
/**
* Name of the image/manifest to push (e.g. username/imagename or imagename)
* Required: false
* Name of the image to push (e.g. username/imagename or imagename)
* Required: true
* Default: None.
*/
IMAGE = "image",
@ -29,13 +29,12 @@ export enum Inputs {
PASSWORD = "password",
/**
* Hostname and optional namespace to push the image to (eg. quay.io/username or quay.io)
* Required: false
* Required: true
* Default: None.
*/
REGISTRY = "registry",
/**
* 'The tag or tags of the image/manifest to push.
* For multiple tags, separate by whitespace. For example, "latest v1"'
* The tag or tags of the image to push. For multiple tags, seperate by a space. For example, "latest v1"
* Required: false
* Default: "latest"
*/
@ -56,7 +55,7 @@ export enum Inputs {
export enum Outputs {
/**
* The pushed image/manifest digest, as written to the "digestfile"
* The pushed image digest, as written to the "digestfile"
* Required: false
* Default: None.
*/

View file

@ -2,14 +2,8 @@ import * as core from "@actions/core";
import * as exec from "@actions/exec";
import * as io from "@actions/io";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import {
isStorageDriverOverlay, findFuseOverlayfsPath,
splitByNewline,
isFullImageName, getFullImageName,
getFullDockerImageName,
} from "./util";
import { splitByNewline } from "./util";
import { Inputs, Outputs } from "./generated/inputs-outputs";
interface ExecResult {
@ -27,10 +21,9 @@ let podmanPath: string | undefined;
// boolean value to check if pushed image is from Docker image storage
let isImageFromDocker = false;
let sourceImages: string[];
let destinationImages: string[];
let dockerPodmanRoot: string;
let dockerPodmanOpts: string[];
let imageToPush: string;
let tagsList: string[];
let dockerBaseUrl: string;
async function getPodmanPath(): Promise<string> {
if (podmanPath == null) {
@ -41,77 +34,31 @@ async function getPodmanPath(): Promise<string> {
return podmanPath;
}
// base URL that gets appended if image is pulled from the Docker imaege storage
const DOCKER_IO = `docker.io`;
const DOCKER_IO_NAMESPACED = DOCKER_IO + `/library`;
async function run(): Promise<void> {
const DEFAULT_TAG = "latest";
const image = core.getInput(Inputs.IMAGE);
const imageInput = core.getInput(Inputs.IMAGE, { required: true });
const tags = core.getInput(Inputs.TAGS);
// split tags
const tagsList = tags.trim().split(/\s+/);
tagsList = tags.split(" ");
// handle the case when image name is 'namespace/imagename' and image is present in docker storage
dockerBaseUrl = imageInput.indexOf("/") > -1 ? DOCKER_IO : DOCKER_IO_NAMESPACED;
// info message if user doesn't provides any tag
if (tagsList.length === 0) {
core.info(`Input "${Inputs.TAGS}" is not provided, using default tag "${DEFAULT_TAG}"`);
tagsList.push(DEFAULT_TAG);
}
const normalizedTagsList: string[] = [];
let isNormalized = false;
for (const tag of tagsList) {
normalizedTagsList.push(tag.toLowerCase());
if (tag.toLowerCase() !== tag) {
isNormalized = true;
}
}
const normalizedImage = image.toLowerCase();
if (isNormalized || image !== normalizedImage) {
core.warning(`Reference to image and/or tag must be lowercase.`
+ ` Reference has been converted to be compliant with standard.`);
}
const registry = core.getInput(Inputs.REGISTRY);
const registry = core.getInput(Inputs.REGISTRY, { required: true });
const username = core.getInput(Inputs.USERNAME);
const password = core.getInput(Inputs.PASSWORD);
const tlsVerify = core.getInput(Inputs.TLS_VERIFY);
const digestFileInput = core.getInput(Inputs.DIGESTFILE);
// check if all tags provided are in `image:tag` format
const isFullImageNameTag = isFullImageName(normalizedTagsList[0]);
if (normalizedTagsList.some((tag) => isFullImageName(tag) !== isFullImageNameTag)) {
throw new Error(`Input "${Inputs.TAGS}" cannot have a mix of full name and non full name tags`);
}
if (!isFullImageNameTag) {
if (!normalizedImage) {
throw new Error(`Input "${Inputs.IMAGE}" must be provided when using non full name tags`);
}
if (!registry) {
throw new Error(`Input "${Inputs.REGISTRY}" must be provided when using non full name tags`);
}
const registryWithoutTrailingSlash = registry.replace(/\/$/, "");
const registryPath = `${registryWithoutTrailingSlash}/${normalizedImage}`;
core.info(`Combining image name "${normalizedImage}" and registry "${registry}" `
+ `to form registry path "${registryPath}"`);
if (normalizedImage.indexOf("/") > -1 && registry.indexOf("/") > -1) {
core.warning(`"${registryPath}" does not seem to be a valid registry path. `
+ `The registry path should not contain more than 2 slashes. `
+ `Refer to the Inputs section of the readme for naming image and registry.`);
}
sourceImages = normalizedTagsList.map((tag) => getFullImageName(normalizedImage, tag));
destinationImages = normalizedTagsList.map((tag) => getFullImageName(registryPath, tag));
}
else {
if (normalizedImage) {
core.warning(`Input "${Inputs.IMAGE}" is ignored when using full name tags`);
}
if (registry) {
core.warning(`Input "${Inputs.REGISTRY}" is ignored when using full name tags`);
}
sourceImages = normalizedTagsList;
destinationImages = normalizedTagsList;
}
const inputExtraArgsStr = core.getInput(Inputs.EXTRA_ARGS);
let podmanExtraArgs: string[] = [];
if (inputExtraArgsStr) {
@ -121,95 +68,94 @@ async function run(): Promise<void> {
podmanExtraArgs = lines.flatMap((line) => line.split(" ")).map((arg) => arg.trim());
}
imageToPush = `${imageInput}`;
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();
// check if image with all the required tags exist in Podman image storage
const podmanImageStorageCheckResult: ImageStorageCheckResult = await checkImageInPodman();
const podmanFoundTags: string[] = podmanImageStorageCheckResult.foundTags;
const podmanMissingTags: string[] = podmanImageStorageCheckResult.missingTags;
const podmanFoundTags: string[] = podmanImageStorageCheckResult.foundTags;
const podmanMissingTags: string[] = podmanImageStorageCheckResult.missingTags;
if (podmanFoundTags.length > 0) {
core.info(`Tag${podmanFoundTags.length !== 1 ? "s" : ""} "${podmanFoundTags.join(", ")}" `
+ `found in Podman image storage`);
}
if (podmanFoundTags.length > 0) {
core.info(`Tag${podmanFoundTags.length !== 1 ? "s" : ""} "${podmanFoundTags.join(", ")}" `
+ `of "${imageToPush}" found in Podman image storage`);
}
// Log warning if few tags are not found
if (podmanMissingTags.length > 0 && podmanFoundTags.length > 0) {
core.warning(`Tag${podmanMissingTags.length !== 1 ? "s" : ""} "${podmanMissingTags.join(", ")}" `
+ `not found in Podman image storage`);
}
// Log warning if few tags are not found
if (podmanMissingTags.length > 0 && podmanFoundTags.length > 0) {
core.warning(`Tag${podmanMissingTags.length !== 1 ? "s" : ""} "${podmanMissingTags.join(", ")}" `
+ `of "${imageToPush}" not found in Podman image storage`);
}
// check if image with all the required tags exist in Docker image storage
// and if exist pull the image with all the tags to Podman
const dockerImageStorageCheckResult: ImageStorageCheckResult = await pullImageFromDocker();
// check if image with all the required tags exist in Docker image storage
// and if exist pull the image with all the tags to Podman
const dockerImageStorageCheckResult: ImageStorageCheckResult = await pullImageFromDocker();
const dockerFoundTags: string[] = dockerImageStorageCheckResult.foundTags;
const dockerMissingTags: string[] = dockerImageStorageCheckResult.missingTags;
const dockerFoundTags: string[] = dockerImageStorageCheckResult.foundTags;
const dockerMissingTags: string[] = dockerImageStorageCheckResult.missingTags;
if (dockerFoundTags.length > 0) {
core.info(`Tag${dockerFoundTags.length !== 1 ? "s" : ""} "${dockerFoundTags.join(", ")}" `
+ `found in Docker image storage`);
}
if (dockerFoundTags.length > 0) {
core.info(`Tag${dockerFoundTags.length !== 1 ? "s" : ""} "${dockerFoundTags.join(", ")}" `
+ `of "${imageToPush}" found in Docker image storage`);
}
// Log warning if few tags are not found
if (dockerMissingTags.length > 0 && dockerFoundTags.length > 0) {
core.warning(`Tag${dockerMissingTags.length !== 1 ? "s" : ""} "${dockerMissingTags.join(", ")}" `
+ `not found in Docker image storage`);
}
// Log warning if few tags are not found
if (dockerMissingTags.length > 0 && dockerFoundTags.length > 0) {
core.warning(`Tag${dockerMissingTags.length !== 1 ? "s" : ""} "${dockerMissingTags.join(", ")}" `
+ `of "${imageToPush}" not found in Docker image storage`);
}
// failing if image with any of the tag is not found in Docker as well as Podman
if (podmanMissingTags.length > 0 && dockerMissingTags.length > 0) {
throw new Error(
`❌ All tags were not found in either Podman image storage, or Docker image storage. `
+ `Tag${podmanMissingTags.length !== 1 ? "s" : ""} "${podmanMissingTags.join(", ")}" `
+ `not found in Podman image storage, and tag${dockerMissingTags.length !== 1 ? "s" : ""} `
+ `"${dockerMissingTags.join(", ")}" not found in Docker image storage.`
);
}
const allTagsinPodman: boolean = podmanFoundTags.length === normalizedTagsList.length;
const allTagsinDocker: boolean = dockerFoundTags.length === normalizedTagsList.length;
if (allTagsinPodman && allTagsinDocker) {
const isPodmanImageLatest = await isPodmanLocalImageLatest();
if (!isPodmanImageLatest) {
core.warning(
`The version of "${sourceImages[0]}" in the Docker image storage is more recent `
+ `than the version in the Podman image storage. The image(s) from the Docker image storage `
+ `will be pushed.`
);
isImageFromDocker = true;
}
else {
core.warning(
`The version of "${sourceImages[0]}" in the Podman image storage is more recent `
+ `than the version in the Docker image storage. The image(s) from the Podman image `
+ `storage will be pushed.`
);
}
}
else if (allTagsinDocker) {
core.info(
`Tag "${sourceImages[0]}" was found in the Docker image storage, but not in the Podman `
+ `image storage. The image(s) will be pulled into Podman image storage, pushed, and then `
+ `removed from the Podman image storage.`
// failing if image with any of the tag is not found in Docker as well as Podman
if (podmanMissingTags.length > 0 && dockerMissingTags.length > 0) {
throw new Error(
`❌ All tags for "${imageToPush}" were not found in either Podman image storage, or Docker image storage. `
+ `Tag${podmanMissingTags.length !== 1 ? "s" : ""} "${podmanMissingTags.join(", ")}" `
+ `not found in Podman image storage, and tag${dockerMissingTags.length !== 1 ? "s" : ""} `
+ `"${dockerMissingTags.join(", ")}" not found in Docker image storage.`
);
}
const allTagsinPodman: boolean = podmanFoundTags.length === tagsList.length;
const allTagsinDocker: boolean = dockerFoundTags.length === tagsList.length;
if (allTagsinPodman && allTagsinDocker) {
const isPodmanImageLatest = await isPodmanLocalImageLatest();
if (!isPodmanImageLatest) {
core.warning(
`The version of "${imageToPush}" in the Docker image storage is more recent `
+ `than the version in the Podman image storage. The image(s) from the Docker image storage `
+ `will be pushed.`
);
imageToPush = `${dockerBaseUrl}/${imageToPush}`;
isImageFromDocker = true;
}
else {
core.info(
`Tag "${sourceImages[0]}" was found in the Podman image storage, but not in the Docker `
+ `image storage. The image(s) will be pushed from Podman image storage.`
core.warning(
`The version of "${imageToPush}" in the Podman image storage is more recent `
+ `than the version in the Docker image storage. The image(s) from the Podman image `
+ `storage will be pushed.`
);
}
}
else if (allTagsinDocker) {
imageToPush = `${dockerBaseUrl}/${imageToPush}`;
core.info(
`"${imageToPush}" was found in the Docker image storage, but not in the Podman `
+ `image storage. The image(s) will be pulled into Podman image storage, pushed, and then `
+ `removed from the Podman image storage.`
);
isImageFromDocker = true;
}
else {
core.info(
`"${imageToPush}" was found in the Podman image storage, but not in the Docker `
+ `image storage. The image(s) will be pushed from Podman image storage.`
);
}
let pushMsg = `⏳ Pushing "${sourceImages.join(", ")}" to "${destinationImages.join(", ")}" respectively`;
let pushMsg = `⏳ Pushing "${imageToPush}" with tag${tagsList.length !== 1 ? "s" : ""} `
+ `"${tagsList.join(", ")}" to "${registry}"`;
if (username) {
pushMsg += ` as "${username}"`;
}
@ -227,34 +173,38 @@ async function run(): Promise<void> {
}
let digestFile = digestFileInput;
const imageNameWithTag = `${imageToPush}:${tagsList[0]}`;
if (!digestFile) {
digestFile = `${sourceImages[0].replace(
digestFile = `${imageNameWithTag.replace(
/[/\\/?%*:|"<>]/g,
"-",
)}_digest.txt`;
}
const registryWithoutTrailingSlash = registry.replace(/\/$/, "");
const registryPath = `${registryWithoutTrailingSlash}/${imageInput}`;
core.info(`Combining image name "${imageInput}" and registry "${registry}" `
+ `to form registry path "${registryPath}"`);
if (imageInput.indexOf("/") > -1 && registry.indexOf("/") > -1) {
core.warning(`"${registryPath}" does not seem to be a valid registry path. `
+ `The registry path should not contain more than 2 slashes. `
+ `Refer to the Inputs section of the readme for naming image and registry.`);
}
// push the image
for (let i = 0; i < destinationImages.length; i++) {
const args = [];
if (isImageFromDocker) {
args.push(...dockerPodmanOpts);
}
if (isManifest) {
args.push("manifest");
}
args.push(...[
for (const tag of tagsList) {
const imageWithTag = `${imageToPush}:${tag}`;
const registryPathWithTag = `${registryPath}:${tag}`;
const args = [
"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");
}
imageWithTag,
registryPathWithTag,
];
if (podmanExtraArgs.length > 0) {
args.push(...podmanExtraArgs);
}
@ -270,9 +220,9 @@ async function run(): Promise<void> {
}
await execute(await getPodmanPath(), args);
core.info(`✅ Successfully pushed "${sourceImages[i]}" to "${destinationImages[i]}"`);
core.info(`✅ Successfully pushed "${imageWithTag}" to "${registryPathWithTag}"`);
registryPathList.push(destinationImages[i]);
registryPathList.push(registryPathWithTag);
try {
const digest = (await fs.promises.readFile(digestFile)).toString();
@ -291,28 +241,29 @@ async function run(): Promise<void> {
}
async function pullImageFromDocker(): Promise<ImageStorageCheckResult> {
core.info(`🔍 Checking if "${sourceImages.join(", ")}" present in the local Docker image storage`);
core.info(`🔍 Checking if "${imageToPush}" with tag${tagsList.length !== 1 ? "s" : ""} `
+ `"${tagsList.join(", ")}" is present in the local Docker image storage`);
let imageWithTag;
const foundTags: string[] = [];
const missingTags: string[] = [];
try {
for (const imageWithTag of sourceImages) {
for (const tag of tagsList) {
imageWithTag = `${imageToPush}:${tag}`;
const commandResult: ExecResult = await execute(
await getPodmanPath(),
[ ...dockerPodmanOpts, "pull", `docker-daemon:${imageWithTag}` ],
[ "pull", `docker-daemon:${imageWithTag}` ],
{ ignoreReturnCode: true, failOnStdErr: false, group: true }
);
if (commandResult.exitCode === 0) {
foundTags.push(imageWithTag);
foundTags.push(tag);
}
else {
missingTags.push(imageWithTag);
missingTags.push(tag);
}
}
}
catch (err) {
if (err instanceof Error) {
core.debug(err.message);
}
core.warning(err);
}
return {
@ -323,28 +274,29 @@ async function pullImageFromDocker(): Promise<ImageStorageCheckResult> {
async function checkImageInPodman(): Promise<ImageStorageCheckResult> {
// check if images exist in Podman's storage
core.info(`🔍 Checking if "${sourceImages.join(", ")}" present in the local Podman image storage`);
core.info(`🔍 Checking if "${imageToPush}" with tag${tagsList.length !== 1 ? "s" : ""} `
+ `"${tagsList.join(", ")}" is present in the local Podman image storage`);
let imageWithTag;
const foundTags: string[] = [];
const missingTags: string[] = [];
try {
for (const imageWithTag of sourceImages) {
for (const tag of tagsList) {
imageWithTag = `${imageToPush}:${tag}`;
const commandResult: ExecResult = await execute(
await getPodmanPath(),
[ "image", "exists", imageWithTag ],
{ ignoreReturnCode: true }
);
if (commandResult.exitCode === 0) {
foundTags.push(imageWithTag);
foundTags.push(tag);
}
else {
missingTags.push(imageWithTag);
missingTags.push(tag);
}
}
}
catch (err) {
if (err instanceof Error) {
core.debug(err.message);
}
core.debug(err);
}
return {
@ -356,7 +308,7 @@ async function checkImageInPodman(): Promise<ImageStorageCheckResult> {
async function isPodmanLocalImageLatest(): Promise<boolean> {
// checking for only one tag as creation time will be
// same for all the tags present
const imageWithTag = sourceImages[0];
const imageWithTag = `${imageToPush}:${tagsList[0]}`;
// get creation time of the image present in the Podman image storage
const podmanLocalImageTimeStamp = await execute(await getPodmanPath(), [
@ -371,10 +323,9 @@ async function isPodmanLocalImageLatest(): Promise<boolean> {
// appending 'docker.io/library' infront of image name as pulled image name
// from Docker image storage starts with the 'docker.io/library'
const pulledImageCreationTimeStamp = await execute(await getPodmanPath(), [
...dockerPodmanOpts,
"image",
"inspect",
getFullDockerImageName(imageWithTag),
`${dockerBaseUrl}/${imageWithTag}`,
"--format",
"{{.Created}}",
]);
@ -386,76 +337,15 @@ async function isPodmanLocalImageLatest(): Promise<boolean> {
return podmanImageTime > dockerImageTime;
}
async function createDockerPodmanImageStroage(): Promise<void> {
core.info(`Creating temporary Podman image storage for pulling from Docker daemon`);
dockerPodmanRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), "podman-from-docker-"));
dockerPodmanOpts = [ "--root", dockerPodmanRoot ];
if (await isStorageDriverOverlay()) {
const fuseOverlayfsPath = await findFuseOverlayfsPath();
if (fuseOverlayfsPath) {
core.info(`Overriding storage mount_program with "fuse-overlayfs" in environment`);
dockerPodmanOpts.push("--storage-opt");
dockerPodmanOpts.push(`overlay.mount_program=${fuseOverlayfsPath}`);
}
else {
core.warning(`"fuse-overlayfs" is not found. Install it before running this action. `
+ `For more detail see https://github.com/redhat-actions/buildah-build/issues/45`);
// remove the pulled image from the Podman image storage
async function removeDockerImage(): Promise<void> {
if (imageToPush) {
core.info(`Removing "${imageToPush}" from the Podman image storage`);
for (const tag of tagsList) {
const imageWithTag = `${imageToPush}:${tag}`;
await execute(await getPodmanPath(), [ "rmi", imageWithTag ]);
}
}
else {
core.info("Storage driver is not 'overlay', so not overriding storage configuration");
}
}
async function removeDockerPodmanImageStroage(): Promise<void> {
if (dockerPodmanRoot) {
try {
core.info(`Removing temporary Podman image storage for pulling from Docker daemon`);
await execute(
await getPodmanPath(),
[ ...dockerPodmanOpts, "rmi", "-a", "-f" ]
);
await fs.promises.rmdir(dockerPodmanRoot, { recursive: true });
}
catch (err) {
core.warning(`Failed to remove podman image stroage ${dockerPodmanRoot}: ${err}`);
}
}
}
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(
@ -510,17 +400,12 @@ async function execute(
}
}
async function main(): Promise<void> {
try {
await createDockerPodmanImageStroage();
await run();
}
finally {
await removeDockerPodmanImageStroage();
}
}
main()
run()
.then(async () => {
if (isImageFromDocker) {
await removeDockerImage();
}
})
.catch((err) => {
core.setFailed(err.message);
});

View file

@ -3,91 +3,6 @@
* Licensed under the MIT License. See LICENSE file in the project root for license information.
**************************************************************************************************/
import * as ini from "ini";
import { promises as fs } from "fs";
import * as core from "@actions/core";
import * as path from "path";
import * as io from "@actions/io";
import * as os from "os";
async function findStorageDriver(filePaths: string[]): Promise<string> {
let storageDriver = "";
for (const filePath of filePaths) {
core.debug(`Checking if the storage file exists at ${filePath}`);
if (await fileExists(filePath)) {
core.debug(`Storage file exists at ${filePath}`);
const fileContent = ini.parse(await fs.readFile(filePath, "utf-8"));
if (fileContent.storage.driver) {
storageDriver = fileContent.storage.driver;
}
}
}
return storageDriver;
}
export async function isStorageDriverOverlay(): Promise<boolean> {
let xdgConfigHome = path.join(os.homedir(), ".config");
if (process.env.XDG_CONFIG_HOME) {
xdgConfigHome = process.env.XDG_CONFIG_HOME;
}
const filePaths: string[] = [
"/etc/containers/storage.conf",
path.join(xdgConfigHome, "containers/storage.conf"),
];
const storageDriver = await findStorageDriver(filePaths);
return (storageDriver === "overlay");
}
async function fileExists(filePath: string): Promise<boolean> {
try {
await fs.access(filePath);
return true;
}
catch (err) {
return false;
}
}
export async function findFuseOverlayfsPath(): Promise<string | undefined> {
let fuseOverlayfsPath;
try {
fuseOverlayfsPath = await io.which("fuse-overlayfs");
}
catch (err) {
if (err instanceof Error) {
core.debug(err.message);
}
}
return fuseOverlayfsPath;
}
export function splitByNewline(s: string): string[] {
return s.split(/\r?\n/);
}
export function isFullImageName(image: string): boolean {
return image.indexOf(":") > 0;
}
export function getFullImageName(image: string, tag: string): string {
if (isFullImageName(tag)) {
return tag;
}
return `${image}:${tag}`;
}
const DOCKER_IO = `docker.io`;
const DOCKER_IO_NAMESPACED = DOCKER_IO + `/library`;
export function getFullDockerImageName(image: string): string {
switch (image.split("/").length) {
case 1:
return `${DOCKER_IO_NAMESPACED}/${image}`;
case 2:
if (image.includes("amazonaws.com")) return image;
return `${DOCKER_IO}/${image}`;
default:
return image;
}
}