mirror of
https://github.com/redhat-actions/push-to-registry.git
synced 2025-02-23 10:31:22 +01:00
Add feature to push multiple tags of the image (#18)
* Modify workflow to trigger on pull request * Update workflow to perform multi tag build and push Signed-off-by: divyansh42 <diagrawa@redhat.com>
This commit is contained in:
parent
23eb62f550
commit
2e6cff9b90
8 changed files with 320 additions and 149 deletions
94
.github/workflows/verify-push.yaml
vendored
94
.github/workflows/verify-push.yaml
vendored
|
@ -1,41 +1,99 @@
|
||||||
# This workflow will perform a test whenever there
|
# This workflow will perform a test whenever there
|
||||||
# is some change in code done to ensure that the changes
|
# is some change in code done to ensure that the changes
|
||||||
# are not buggy and we are getting the desired output.
|
# are not buggy and we are getting the desired output.
|
||||||
name: Test Push without image
|
name: Test Build and Push
|
||||||
on: [ push, workflow_dispatch ]
|
on: [ push, workflow_dispatch, pull_request_target ]
|
||||||
env:
|
env:
|
||||||
IMAGE_NAME: myimage
|
PROJECT_DIR: spring-petclinic
|
||||||
|
IMAGE_NAME: spring-petclinic
|
||||||
IMAGE_REGISTRY: quay.io
|
IMAGE_REGISTRY: quay.io
|
||||||
IMAGE_TAG: latest
|
IMAGE_TAGS: v1 ${{ github.sha }}
|
||||||
|
MVN_REPO_DIR: ~/.m2/repository
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build-and-push:
|
||||||
name: Push image to Quay.io
|
name: Build and push image to Quay.io
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
# Checkout push-to-registry action github repository
|
# Checkout push-to-registry action github repository
|
||||||
- name: Checkout Push to Registry action
|
- name: Checkout Push to Registry action
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: "push-to-registry"
|
||||||
|
|
||||||
- name: Build Image using Docker
|
# Checkout spring-petclinic github repository
|
||||||
|
- name: Checkout spring-petclinic project
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: "spring-projects/spring-petclinic"
|
||||||
|
path: ${{ env.PROJECT_DIR }}
|
||||||
|
|
||||||
|
# If none of these files has changed, we assume that the contents of
|
||||||
|
# .m2/repository can be fetched from the cache.
|
||||||
|
- name: Hash Maven files
|
||||||
|
working-directory: ${{ env.PROJECT_DIR }}
|
||||||
run: |
|
run: |
|
||||||
docker build -t ${{ env.IMAGE_NAME }}:latest -<<EOF
|
echo "MVN_HASH=${{ hashFiles('**/pom.xml', '.mvn/**/*', 'mvnw*') }}" >> $GITHUB_ENV
|
||||||
FROM busybox
|
|
||||||
RUN echo "hello world"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Push the image to image registry
|
# Download the m2 repository from the cache to speed up the build.
|
||||||
- name: Push To Quay
|
- name: Check for Maven cache
|
||||||
uses: ./
|
id: check-mvn-cache
|
||||||
id: push
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ${{ env.MVN_REPO_DIR }}
|
||||||
|
key: ${{ env.MVN_HASH }}
|
||||||
|
|
||||||
|
# Setup java.
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 11
|
||||||
|
|
||||||
|
# Run maven to build the project
|
||||||
|
- name: Maven
|
||||||
|
working-directory: ${{ env.PROJECT_DIR }}
|
||||||
|
run: |
|
||||||
|
mvn package -ntp -B
|
||||||
|
|
||||||
|
# If there was no cache hit above, store the output into the cache now.
|
||||||
|
- name: Save Maven repo into cache
|
||||||
|
if: ${{ steps.check-mvn-cache.outputs.cache-hit }} != 'true'
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ${{ env.MVN_REPO_DIR }}
|
||||||
|
key: ${{ env.MVN_HASH }}
|
||||||
|
|
||||||
|
# Build image using Buildah action
|
||||||
|
- name: Build Image
|
||||||
|
id: build_image
|
||||||
|
uses: redhat-actions/buildah-build@main
|
||||||
with:
|
with:
|
||||||
image: ${{ env.IMAGE_NAME }}
|
image: ${{ env.IMAGE_NAME }}
|
||||||
tag: ${{ env.IMAGE_TAG }}
|
tags: ${{ env.IMAGE_TAGS }}
|
||||||
|
base-image: 'registry.access.redhat.com/openjdk/openjdk-11-rhel7'
|
||||||
|
# To avoid hardcoding a particular version of the binary.
|
||||||
|
content: |
|
||||||
|
./spring-petclinic/target/spring-petclinic-*.jar
|
||||||
|
entrypoint: |
|
||||||
|
java
|
||||||
|
-jar
|
||||||
|
spring-petclinic-*.jar
|
||||||
|
port: 8080
|
||||||
|
oci: 'true'
|
||||||
|
|
||||||
|
# Push the image to Quay.io (Image Registry)
|
||||||
|
- name: Push To Quay
|
||||||
|
uses: ./push-to-registry/
|
||||||
|
id: push
|
||||||
|
with:
|
||||||
|
image: ${{ steps.build_image.outputs.image }}
|
||||||
|
tags: ${{ steps.build_image.outputs.tags }}
|
||||||
registry: ${{ env.IMAGE_REGISTRY }}/${{ secrets.REGISTRY_USER }}
|
registry: ${{ env.IMAGE_REGISTRY }}/${{ secrets.REGISTRY_USER }}
|
||||||
username: ${{ secrets.REGISTRY_USER }}
|
username: ${{ secrets.REGISTRY_USER }}
|
||||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
- name: Echo outputs
|
- name: Echo outputs
|
||||||
run: |
|
run: |
|
||||||
echo "registry-path ${{ steps.push.outputs.registry-path }}"
|
echo "Digest: ${{ steps.push.outputs.digest }}"
|
||||||
echo "digest ${{ steps.push.outputs.digest }}"
|
echo "Registry Paths: ${{ steps.push.outputs.registry-paths }}"
|
||||||
|
|
19
README.md
19
README.md
|
@ -31,10 +31,10 @@ Refer to the [`podman push`](http://docs.podman.io/en/latest/markdown/podman-man
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>tag</td>
|
<td>tags</td>
|
||||||
<td>No</td>
|
<td>No</td>
|
||||||
<td>
|
<td>
|
||||||
Image tag to push.<br>
|
The tag or tags of the image to push. For multiple tags, seperate by a space. For example, <code>latest ${{ github.sha }}</code><br>
|
||||||
Defaults to <code>latest</code>.
|
Defaults to <code>latest</code>.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -73,11 +73,12 @@ Refer to the [`podman push`](http://docs.podman.io/en/latest/markdown/podman-man
|
||||||
|
|
||||||
## Action Outputs
|
## Action Outputs
|
||||||
|
|
||||||
`registry-path`: The registry path to which the image was pushed.<br>
|
`registry-paths`: A JSON array of registry paths to which the tag(s) were pushed.<br>
|
||||||
For example, `quay.io/username/spring-image:v1`.
|
For example, `[ quay.io/username/spring-image:v1, quay.io/username/spring-image:latest ]`.
|
||||||
|
|
||||||
`digest`: The pushed image digest, as written to the `digestfile`.<br>
|
`digest`: The pushed image digest, as written to the `digestfile`.<br>
|
||||||
For example, `sha256:66ce924069ec4181725d15aa27f34afbaf082f434f448dc07a42daa3305cdab3`.
|
For example, `sha256:66ce924069ec4181725d15aa27f34afbaf082f434f448dc07a42daa3305cdab3`.
|
||||||
|
For multiple tags, the digest is the same.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
@ -93,16 +94,18 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
IMAGE_NAME: my-app
|
IMAGE_NAME: my-app
|
||||||
IMAGE_TAG: latest
|
IMAGE_TAGS: latest v1
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Build Image
|
- name: Build Image
|
||||||
|
id: build-image
|
||||||
uses: redhat-actions/buildah-build@v1
|
uses: redhat-actions/buildah-build@v1
|
||||||
with:
|
with:
|
||||||
image: ${{ env.IMAGE_NAME }}
|
image: ${{ env.IMAGE_NAME }}
|
||||||
tag: ${{ env.TAG }}
|
tags: ${{ env.IMAGE_TAGS }}
|
||||||
|
base-image: some_image
|
||||||
dockerfiles: |
|
dockerfiles: |
|
||||||
./Dockerfile
|
./Dockerfile
|
||||||
|
|
||||||
|
@ -110,8 +113,8 @@ jobs:
|
||||||
id: push-to-quay
|
id: push-to-quay
|
||||||
uses: redhat-actions/push-to-registry@v1
|
uses: redhat-actions/push-to-registry@v1
|
||||||
with:
|
with:
|
||||||
image: ${{ env.IMAGE_NAME }}
|
image: ${{ steps.build-image.outputs.image }}
|
||||||
tag: ${{ env.TAG }}
|
tags: ${{ steps.build-image.outputs.tags }}
|
||||||
registry: ${{ secrets.QUAY_REPO }}
|
registry: ${{ secrets.QUAY_REPO }}
|
||||||
username: ${{ secrets.QUAY_USERNAME }}
|
username: ${{ secrets.QUAY_USERNAME }}
|
||||||
password: ${{ secrets.QUAY_TOKEN }}
|
password: ${{ secrets.QUAY_TOKEN }}
|
||||||
|
|
16
action.yml
16
action.yml
|
@ -8,8 +8,8 @@ inputs:
|
||||||
image:
|
image:
|
||||||
description: 'Name of the image to push'
|
description: 'Name of the image to push'
|
||||||
required: true
|
required: true
|
||||||
tag:
|
tags:
|
||||||
description: 'Tag of the image to push'
|
description: 'The tag or tags of the image to push. For multiple tags, seperate by a space. For example, "latest v1"'
|
||||||
required: false
|
required: false
|
||||||
default: 'latest'
|
default: 'latest'
|
||||||
registry:
|
registry:
|
||||||
|
@ -25,10 +25,18 @@ inputs:
|
||||||
description: 'Verify TLS certificates when contacting the registry'
|
description: 'Verify TLS certificates when contacting the registry'
|
||||||
required: false
|
required: false
|
||||||
default: 'true'
|
default: 'true'
|
||||||
|
digestfile:
|
||||||
|
description: |
|
||||||
|
After copying the image, write the digest of the resulting image to the file.
|
||||||
|
By default, the filename will be determined from the image and tag.
|
||||||
|
The contents of this file are the digest output.
|
||||||
|
required: false
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
registry-path:
|
digest:
|
||||||
description: 'The registry path to which the image was pushed'
|
description: 'The pushed image digest, as written to the "digestfile"'
|
||||||
|
registry-paths:
|
||||||
|
description: 'A JSON array of registry paths to which the tag(s) were pushed'
|
||||||
runs:
|
runs:
|
||||||
using: 'node12'
|
using: 'node12'
|
||||||
main: 'dist/index.js'
|
main: 'dist/index.js'
|
||||||
|
|
2
dist/index.js
vendored
2
dist/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/index.js.map
vendored
2
dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
98
package-lock.json
generated
98
package-lock.json
generated
|
@ -114,9 +114,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@redhat-actions/eslint-config": {
|
"@redhat-actions/eslint-config": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/@redhat-actions/eslint-config/-/eslint-config-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@redhat-actions/eslint-config/-/eslint-config-1.2.11.tgz",
|
||||||
"integrity": "sha512-HFpdgXIB01wmZbCsEpaY0UhQlRBiM/kJxO0WCufIx/wTxepewwsLTsgCgKkljbGfSaqN7FMJ/TbwY1CY/ltHcw==",
|
"integrity": "sha512-hS8XXkpWu32Z3S6+EFAvD32+QruxyLiSfqrXjkaSLtWZndkJLNZ/xpHcEwRM1u7KJzd1f7jPl46+GLFfrPT8RQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"eslint-config-airbnb-base": ">= 14",
|
"eslint-config-airbnb-base": ">= 14",
|
||||||
|
@ -130,9 +130,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/json-schema": {
|
"@types/json-schema": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
|
||||||
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
|
"integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/json5": {
|
"@types/json5": {
|
||||||
|
@ -148,13 +148,13 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@typescript-eslint/eslint-plugin": {
|
"@typescript-eslint/eslint-plugin": {
|
||||||
"version": "4.14.0",
|
"version": "4.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.1.tgz",
|
||||||
"integrity": "sha512-IJ5e2W7uFNfg4qh9eHkHRUCbgZ8VKtGwD07kannJvM5t/GU8P8+24NX8gi3Hf5jST5oWPY8kyV1s/WtfiZ4+Ww==",
|
"integrity": "sha512-5JriGbYhtqMS1kRcZTQxndz1lKMwwEXKbwZbkUZNnp6MJX0+OVXnG0kOlBZP4LUAxEyzu3cs+EXd/97MJXsGfw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/experimental-utils": "4.14.0",
|
"@typescript-eslint/experimental-utils": "4.14.1",
|
||||||
"@typescript-eslint/scope-manager": "4.14.0",
|
"@typescript-eslint/scope-manager": "4.14.1",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"functional-red-black-tree": "^1.0.1",
|
"functional-red-black-tree": "^1.0.1",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
|
@ -164,55 +164,55 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/experimental-utils": {
|
"@typescript-eslint/experimental-utils": {
|
||||||
"version": "4.14.0",
|
"version": "4.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.1.tgz",
|
||||||
"integrity": "sha512-6i6eAoiPlXMKRbXzvoQD5Yn9L7k9ezzGRvzC/x1V3650rUk3c3AOjQyGYyF9BDxQQDK2ElmKOZRD0CbtdkMzQQ==",
|
"integrity": "sha512-2CuHWOJwvpw0LofbyG5gvYjEyoJeSvVH2PnfUQSn0KQr4v8Dql2pr43ohmx4fdPQ/eVoTSFjTi/bsGEXl/zUUQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/json-schema": "^7.0.3",
|
"@types/json-schema": "^7.0.3",
|
||||||
"@typescript-eslint/scope-manager": "4.14.0",
|
"@typescript-eslint/scope-manager": "4.14.1",
|
||||||
"@typescript-eslint/types": "4.14.0",
|
"@typescript-eslint/types": "4.14.1",
|
||||||
"@typescript-eslint/typescript-estree": "4.14.0",
|
"@typescript-eslint/typescript-estree": "4.14.1",
|
||||||
"eslint-scope": "^5.0.0",
|
"eslint-scope": "^5.0.0",
|
||||||
"eslint-utils": "^2.0.0"
|
"eslint-utils": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/parser": {
|
"@typescript-eslint/parser": {
|
||||||
"version": "4.14.0",
|
"version": "4.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.14.1.tgz",
|
||||||
"integrity": "sha512-sUDeuCjBU+ZF3Lzw0hphTyScmDDJ5QVkyE21pRoBo8iDl7WBtVFS+WDN3blY1CH3SBt7EmYCw6wfmJjF0l/uYg==",
|
"integrity": "sha512-mL3+gU18g9JPsHZuKMZ8Z0Ss9YP1S5xYZ7n68Z98GnPq02pYNQuRXL85b9GYhl6jpdvUc45Km7hAl71vybjUmw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/scope-manager": "4.14.0",
|
"@typescript-eslint/scope-manager": "4.14.1",
|
||||||
"@typescript-eslint/types": "4.14.0",
|
"@typescript-eslint/types": "4.14.1",
|
||||||
"@typescript-eslint/typescript-estree": "4.14.0",
|
"@typescript-eslint/typescript-estree": "4.14.1",
|
||||||
"debug": "^4.1.1"
|
"debug": "^4.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/scope-manager": {
|
"@typescript-eslint/scope-manager": {
|
||||||
"version": "4.14.0",
|
"version": "4.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.1.tgz",
|
||||||
"integrity": "sha512-/J+LlRMdbPh4RdL4hfP1eCwHN5bAhFAGOTsvE6SxsrM/47XQiPSgF5MDgLyp/i9kbZV9Lx80DW0OpPkzL+uf8Q==",
|
"integrity": "sha512-F4bjJcSqXqHnC9JGUlnqSa3fC2YH5zTtmACS1Hk+WX/nFB0guuynVK5ev35D4XZbdKjulXBAQMyRr216kmxghw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "4.14.0",
|
"@typescript-eslint/types": "4.14.1",
|
||||||
"@typescript-eslint/visitor-keys": "4.14.0"
|
"@typescript-eslint/visitor-keys": "4.14.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/types": {
|
"@typescript-eslint/types": {
|
||||||
"version": "4.14.0",
|
"version": "4.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.1.tgz",
|
||||||
"integrity": "sha512-VsQE4VvpldHrTFuVPY1ZnHn/Txw6cZGjL48e+iBxTi2ksa9DmebKjAeFmTVAYoSkTk7gjA7UqJ7pIsyifTsI4A==",
|
"integrity": "sha512-SkhzHdI/AllAgQSxXM89XwS1Tkic7csPdndUuTKabEwRcEfR8uQ/iPA3Dgio1rqsV3jtqZhY0QQni8rLswJM2w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@typescript-eslint/typescript-estree": {
|
"@typescript-eslint/typescript-estree": {
|
||||||
"version": "4.14.0",
|
"version": "4.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.1.tgz",
|
||||||
"integrity": "sha512-wRjZ5qLao+bvS2F7pX4qi2oLcOONIB+ru8RGBieDptq/SudYwshveORwCVU4/yMAd4GK7Fsf8Uq1tjV838erag==",
|
"integrity": "sha512-M8+7MbzKC1PvJIA8kR2sSBnex8bsR5auatLCnVlNTJczmJgqRn8M+sAlQfkEq7M4IY3WmaNJ+LJjPVRrREVSHQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "4.14.0",
|
"@typescript-eslint/types": "4.14.1",
|
||||||
"@typescript-eslint/visitor-keys": "4.14.0",
|
"@typescript-eslint/visitor-keys": "4.14.1",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"globby": "^11.0.1",
|
"globby": "^11.0.1",
|
||||||
"is-glob": "^4.0.1",
|
"is-glob": "^4.0.1",
|
||||||
|
@ -222,12 +222,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/visitor-keys": {
|
"@typescript-eslint/visitor-keys": {
|
||||||
"version": "4.14.0",
|
"version": "4.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.1.tgz",
|
||||||
"integrity": "sha512-MeHHzUyRI50DuiPgV9+LxcM52FCJFYjJiWHtXlbyC27b80mfOwKeiKI+MHOTEpcpfmoPFm/vvQS88bYIx6PZTA==",
|
"integrity": "sha512-TAblbDXOI7bd0C/9PE1G+AFo7R5uc+ty1ArDoxmrC1ah61Hn6shURKy7gLdRb1qKJmjHkqu5Oq+e4Kt0jwf1IA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "4.14.0",
|
"@typescript-eslint/types": "4.14.1",
|
||||||
"eslint-visitor-keys": "^2.0.0"
|
"eslint-visitor-keys": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -917,9 +917,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flatted": {
|
"flatted": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz",
|
||||||
"integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==",
|
"integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"fs.realpath": {
|
"fs.realpath": {
|
||||||
|
@ -941,9 +941,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"get-intrinsic": {
|
"get-intrinsic": {
|
||||||
"version": "1.0.2",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.0.tgz",
|
||||||
"integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==",
|
"integrity": "sha512-M11rgtQp5GZMZzDL7jLTNxbDfurpzuau5uqRWDPvlHjfvg3TdScAZo96GLvhMjImrmR8uAt0FS2RLoMrfWGKlg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"function-bind": "^1.1.1",
|
"function-bind": "^1.1.1",
|
||||||
|
@ -1806,9 +1806,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"tsutils": {
|
"tsutils": {
|
||||||
"version": "3.19.1",
|
"version": "3.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.20.0.tgz",
|
||||||
"integrity": "sha512-GEdoBf5XI324lu7ycad7s6laADfnAqCw6wLGI+knxvw9vsIYBaJfYdmeCEG3FMMUiSm3OGgNb+m6utsWf5h9Vw==",
|
"integrity": "sha512-RYbuQuvkhuqVeXweWT3tJLKOEJ/UUw9GjNEZGWdrLLlM+611o1gwLHBpxoFJKKl25fLprp2eVthtKs5JOrNeXg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^1.8.1"
|
"tslib": "^1.8.1"
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
"@actions/io": "^1.0.2"
|
"@actions/io": "^1.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@redhat-actions/eslint-config": "^1.2.0",
|
"@redhat-actions/eslint-config": "^1.2.11",
|
||||||
"@redhat-actions/tsconfig": "^1.1.0",
|
"@redhat-actions/tsconfig": "^1.1.0",
|
||||||
"@types/node": "^12.12.7",
|
"@types/node": "^12.12.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.14.0",
|
"@typescript-eslint/eslint-plugin": "^4.14.1",
|
||||||
"@typescript-eslint/parser": "^4.14.0",
|
"@typescript-eslint/parser": "^4.14.1",
|
||||||
"@vercel/ncc": "^0.25.1",
|
"@vercel/ncc": "^0.25.1",
|
||||||
"eslint": "^7.18.0",
|
"eslint": "^7.18.0",
|
||||||
"typescript": "^4.0.5"
|
"typescript": "^4.0.5"
|
||||||
|
|
230
src/index.ts
230
src/index.ts
|
@ -10,11 +10,17 @@ interface ExecResult {
|
||||||
stderr: string;
|
stderr: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ImageStorageCheckResult {
|
||||||
|
readonly foundTags: string[];
|
||||||
|
readonly missingTags: string[];
|
||||||
|
}
|
||||||
|
|
||||||
let podmanPath: string | undefined;
|
let podmanPath: string | undefined;
|
||||||
|
|
||||||
// boolean value to check if pushed image is from Docker image storage
|
// boolean value to check if pushed image is from Docker image storage
|
||||||
let isImageFromDocker = false;
|
let isImageFromDocker = false;
|
||||||
let imageToPush: string;
|
let imageToPush: string;
|
||||||
|
let tagsList: string[];
|
||||||
|
|
||||||
async function getPodmanPath(): Promise<string> {
|
async function getPodmanPath(): Promise<string> {
|
||||||
if (podmanPath == null) {
|
if (podmanPath == null) {
|
||||||
|
@ -29,90 +35,141 @@ const dockerBaseUrl = "docker.io/library";
|
||||||
|
|
||||||
async function run(): Promise<void> {
|
async function run(): Promise<void> {
|
||||||
const imageInput = core.getInput("image", { required: true });
|
const imageInput = core.getInput("image", { required: true });
|
||||||
const tag = core.getInput("tag") || "latest";
|
const tags = core.getInput("tags") || "latest";
|
||||||
|
// split tags
|
||||||
|
tagsList = tags.split(" ");
|
||||||
const registry = core.getInput("registry", { required: true });
|
const registry = core.getInput("registry", { required: true });
|
||||||
const username = core.getInput("username", { required: true });
|
const username = core.getInput("username", { required: true });
|
||||||
const password = core.getInput("password", { required: true });
|
const password = core.getInput("password", { required: true });
|
||||||
const tlsVerify = core.getInput("tls-verify");
|
const tlsVerify = core.getInput("tls-verify");
|
||||||
const digestFileInput = core.getInput("digestfile");
|
const digestFileInput = core.getInput("digestfile");
|
||||||
|
|
||||||
imageToPush = `${imageInput}:${tag}`;
|
imageToPush = `${imageInput}`;
|
||||||
|
const registryPathList: string[] = [];
|
||||||
|
|
||||||
// check if image exist in Podman image storage
|
// check if image with all the required tags exist in Podman image storage
|
||||||
const isPresentInPodman: boolean = await checkImageInPodman();
|
const podmanImageStorageCheckResult: ImageStorageCheckResult = await checkImageInPodman();
|
||||||
|
|
||||||
// check if image exist in Docker image storage and if exist pull the image to Podman
|
const podmanFoundTags: string[] = podmanImageStorageCheckResult.foundTags;
|
||||||
const isPresentInDocker: boolean = await pullImageFromDocker();
|
const podmanMissingTags: string[] = podmanImageStorageCheckResult.missingTags;
|
||||||
|
|
||||||
// failing if image is not found in Docker as well as Podman
|
if (podmanFoundTags.length > 0) {
|
||||||
if (!isPresentInDocker && !isPresentInPodman) {
|
core.info(`Tag(s) ${podmanFoundTags.join(", ")} of ${imageToPush} found in Podman image storage`);
|
||||||
throw new Error(`${imageToPush} not found in Podman local storage, or Docker local storage.`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPresentInPodman && isPresentInDocker) {
|
// Log warning if few tags are found
|
||||||
|
if (podmanMissingTags.length > 0 && podmanFoundTags.length > 0) {
|
||||||
|
core.warning(`Tag(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();
|
||||||
|
|
||||||
|
const dockerFoundTags: string[] = dockerImageStorageCheckResult.foundTags;
|
||||||
|
const dockerMissingTags: string[] = dockerImageStorageCheckResult.missingTags;
|
||||||
|
|
||||||
|
if (dockerFoundTags.length > 0) {
|
||||||
|
core.info(`Tag(s) ${dockerFoundTags.join(", ")} of ${imageToPush} found in Docker image storage`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log warning if few tags are found
|
||||||
|
if (dockerMissingTags.length > 0 && dockerFoundTags.length > 0) {
|
||||||
|
core.warning(`Tag(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(
|
||||||
|
`Tag(s) ${podmanMissingTags.join(", ")} of ${imageToPush} not found in Podman image storage `
|
||||||
|
+ `and Tag(s) ${dockerMissingTags.join(", ")} of ${imageToPush} 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();
|
const isPodmanImageLatest = await isPodmanLocalImageLatest();
|
||||||
if (!isPodmanImageLatest) {
|
if (!isPodmanImageLatest) {
|
||||||
core.warning(`The version of ${imageToPush} in the Docker image storage is more recent `
|
core.warning(
|
||||||
+ `than the version in the Podman image storage. The image from the Docker image storage `
|
`The version of ${imageToPush} in the Docker image storage is more recent `
|
||||||
+ `will be pushed.`);
|
+ `than the version in the Podman image storage. The image(s) from the Docker image storage `
|
||||||
|
+ `will be pushed.`
|
||||||
|
);
|
||||||
imageToPush = `${dockerBaseUrl}/${imageToPush}`;
|
imageToPush = `${dockerBaseUrl}/${imageToPush}`;
|
||||||
isImageFromDocker = true;
|
isImageFromDocker = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core.warning(`The version of ${imageToPush} in the Podman image storage is more recent `
|
core.warning(
|
||||||
+ `than the version in the Docker image storage. The image from the Podman image `
|
`The version of ${imageToPush} in the Podman image storage is more recent `
|
||||||
+ `storage will be pushed.`);
|
+ `than the version in the Docker image storage. The image(s) from the Podman image `
|
||||||
|
+ `storage will be pushed.`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (isPresentInDocker) {
|
else if (allTagsinDocker) {
|
||||||
imageToPush = `${dockerBaseUrl}/${imageToPush}`;
|
imageToPush = `${dockerBaseUrl}/${imageToPush}`;
|
||||||
core.info(`${imageToPush} was found in the Docker image storage, but not in the Podman `
|
core.info(
|
||||||
+ `image storage. The image will be pulled into Podman image storage, pushed, and then `
|
`${imageToPush} was found in the Docker image storage, but not in the Podman `
|
||||||
+ `removed from the Podman image storage.`);
|
+ `image storage. The image(s) will be pulled into Podman image storage, pushed, and then `
|
||||||
|
+ `removed from the Podman image storage.`
|
||||||
|
);
|
||||||
isImageFromDocker = true;
|
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 ${imageToPush} to ${registry}`;
|
let pushMsg = `Pushing ${imageToPush} with tags ${tagsList.join(", ")} to ${registry}`;
|
||||||
if (username) {
|
if (username) {
|
||||||
pushMsg += ` as ${username}`;
|
pushMsg += ` as ${username}`;
|
||||||
}
|
}
|
||||||
core.info(pushMsg);
|
core.info(pushMsg);
|
||||||
|
|
||||||
const registryWithoutTrailingSlash = registry.replace(/\/$/, "");
|
const registryWithoutTrailingSlash = registry.replace(/\/$/, "");
|
||||||
const registryPath = `${registryWithoutTrailingSlash}/${imageInput}:${tag}`;
|
|
||||||
|
|
||||||
const creds = `${username}:${password}`;
|
const creds = `${username}:${password}`;
|
||||||
|
|
||||||
let digestFile = digestFileInput;
|
let digestFile = digestFileInput;
|
||||||
|
const imageNameWithTag = `${imageToPush}:${tagsList[0]}`;
|
||||||
if (!digestFile) {
|
if (!digestFile) {
|
||||||
digestFile = `${imageToPush.replace(
|
digestFile = `${imageNameWithTag.replace(
|
||||||
/[/\\/?%*:|"<>]/g,
|
/[/\\/?%*:|"<>]/g,
|
||||||
"-",
|
"-",
|
||||||
)}_digest.txt`;
|
)}_digest.txt`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// push the image
|
// push the image
|
||||||
const args = [
|
for (const tag of tagsList) {
|
||||||
"push",
|
const imageWithTag = `${imageToPush}:${tag}`;
|
||||||
"--quiet",
|
const registryPath = `${registryWithoutTrailingSlash}/${imageInput}:${tag}`;
|
||||||
"--digestfile",
|
|
||||||
digestFile,
|
|
||||||
"--creds",
|
|
||||||
creds,
|
|
||||||
imageToPush,
|
|
||||||
registryPath,
|
|
||||||
];
|
|
||||||
|
|
||||||
// check if tls-verify is not set to null
|
const args = [
|
||||||
if (tlsVerify) {
|
"push",
|
||||||
args.push(`--tls-verify=${tlsVerify}`);
|
"--quiet",
|
||||||
|
"--digestfile",
|
||||||
|
digestFile,
|
||||||
|
"--creds",
|
||||||
|
creds,
|
||||||
|
imageWithTag,
|
||||||
|
registryPath,
|
||||||
|
];
|
||||||
|
|
||||||
|
// check if tls-verify is not set to null
|
||||||
|
if (tlsVerify) {
|
||||||
|
args.push(`--tls-verify=${tlsVerify}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await execute(await getPodmanPath(), args);
|
||||||
|
core.info(`Successfully pushed ${imageWithTag} to ${registryPath}`);
|
||||||
|
|
||||||
|
registryPathList.push(registryPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
await execute(await getPodmanPath(), args);
|
|
||||||
|
|
||||||
core.info(`Successfully pushed ${imageToPush} to ${registryPath}.`);
|
|
||||||
core.setOutput("registry-path", registryPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const digest = (await fs.promises.readFile(digestFile)).toString();
|
const digest = (await fs.promises.readFile(digestFile)).toString();
|
||||||
core.info(digest);
|
core.info(digest);
|
||||||
|
@ -121,41 +178,83 @@ async function run(): Promise<void> {
|
||||||
catch (err) {
|
catch (err) {
|
||||||
core.warning(`Failed to read digest file "${digestFile}": ${err}`);
|
core.warning(`Failed to read digest file "${digestFile}": ${err}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
core.setOutput("registry-paths", JSON.stringify(registryPathList));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pullImageFromDocker(): Promise<boolean> {
|
async function pullImageFromDocker(): Promise<ImageStorageCheckResult> {
|
||||||
|
core.info(`Checking if ${imageToPush} with tag(s) ${tagsList.join(", ")} is present in Docker image storage`);
|
||||||
|
let imageWithTag;
|
||||||
|
const foundTags: string[] = [];
|
||||||
|
const missingTags: string[] = [];
|
||||||
try {
|
try {
|
||||||
await execute(await getPodmanPath(), [ "pull", `docker-daemon:${imageToPush}` ]);
|
for (const tag of tagsList) {
|
||||||
core.info(`${imageToPush} found in Docker image storage`);
|
imageWithTag = `${imageToPush}:${tag}`;
|
||||||
return true;
|
const commandResult: ExecResult = await execute(
|
||||||
|
await getPodmanPath(),
|
||||||
|
[ "pull", `docker-daemon:${imageWithTag}` ],
|
||||||
|
{ ignoreReturnCode: true, failOnStdErr: false }
|
||||||
|
);
|
||||||
|
if (!commandResult.exitCode) {
|
||||||
|
foundTags.push(tag);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
missingTags.push(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
core.info(`${imageToPush} not found in Docker image storage`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function checkImageInPodman(): Promise<boolean> {
|
|
||||||
// check if images exist in Podman's storage
|
|
||||||
core.info(`Checking if ${imageToPush} is in Podman image storage`);
|
|
||||||
try {
|
|
||||||
await execute(await getPodmanPath(), [ "image", "exists", imageToPush ]);
|
|
||||||
core.info(`${imageToPush} found in Podman image storage`);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
core.info(`${imageToPush} not found in Podman image storage`);
|
|
||||||
core.debug(err);
|
core.debug(err);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
foundTags,
|
||||||
|
missingTags,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkImageInPodman(): Promise<ImageStorageCheckResult> {
|
||||||
|
// check if images exist in Podman's storage
|
||||||
|
core.info(`Checking if ${imageToPush} with tag(s) ${tagsList.join(", ")} is present in Podman image storage`);
|
||||||
|
let imageWithTag;
|
||||||
|
const foundTags: string[] = [];
|
||||||
|
const missingTags: string[] = [];
|
||||||
|
try {
|
||||||
|
for (const tag of tagsList) {
|
||||||
|
imageWithTag = `${imageToPush}:${tag}`;
|
||||||
|
const commandResult: ExecResult = await execute(
|
||||||
|
await getPodmanPath(),
|
||||||
|
[ "image", "exists", imageWithTag ],
|
||||||
|
{ ignoreReturnCode: true }
|
||||||
|
);
|
||||||
|
if (!commandResult.exitCode) {
|
||||||
|
foundTags.push(tag);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
missingTags.push(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
core.debug(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
foundTags,
|
||||||
|
missingTags,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function isPodmanLocalImageLatest(): Promise<boolean> {
|
async function isPodmanLocalImageLatest(): Promise<boolean> {
|
||||||
|
// checking for only one tag as creation time will be
|
||||||
|
// same for all the tags present
|
||||||
|
const imageWithTag = `${imageToPush}:${tagsList[0]}`;
|
||||||
|
|
||||||
// get creation time of the image present in the Podman image storage
|
// get creation time of the image present in the Podman image storage
|
||||||
const podmanLocalImageTimeStamp = await execute(await getPodmanPath(), [
|
const podmanLocalImageTimeStamp = await execute(await getPodmanPath(), [
|
||||||
"image",
|
"image",
|
||||||
"inspect",
|
"inspect",
|
||||||
imageToPush,
|
imageWithTag,
|
||||||
"--format",
|
"--format",
|
||||||
"{{.Created}}",
|
"{{.Created}}",
|
||||||
]);
|
]);
|
||||||
|
@ -166,7 +265,7 @@ async function isPodmanLocalImageLatest(): Promise<boolean> {
|
||||||
const pulledImageCreationTimeStamp = await execute(await getPodmanPath(), [
|
const pulledImageCreationTimeStamp = await execute(await getPodmanPath(), [
|
||||||
"image",
|
"image",
|
||||||
"inspect",
|
"inspect",
|
||||||
`${dockerBaseUrl}/${imageToPush}`,
|
`${dockerBaseUrl}/${imageWithTag}`,
|
||||||
"--format",
|
"--format",
|
||||||
"{{.Created}}",
|
"{{.Created}}",
|
||||||
]);
|
]);
|
||||||
|
@ -182,7 +281,10 @@ async function isPodmanLocalImageLatest(): Promise<boolean> {
|
||||||
async function removeDockerImage(): Promise<void> {
|
async function removeDockerImage(): Promise<void> {
|
||||||
if (imageToPush) {
|
if (imageToPush) {
|
||||||
core.info(`Removing ${imageToPush} from the Podman image storage`);
|
core.info(`Removing ${imageToPush} from the Podman image storage`);
|
||||||
await execute(await getPodmanPath(), [ "rmi", imageToPush ]);
|
for (const tag of tagsList) {
|
||||||
|
const imageWithTag = `${imageToPush}:${tag}`;
|
||||||
|
await execute(await getPodmanPath(), [ "rmi", imageWithTag ]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue