mirror of
https://github.com/redhat-actions/push-to-registry.git
synced 2025-03-31 23:07:47 +02:00
Merge 69b4e0beeb
into 5ed88d269c
This commit is contained in:
commit
f3368ef416
7 changed files with 113 additions and 24 deletions
18
README.md
18
README.md
|
@ -25,18 +25,21 @@ 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
|
||||
| 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
|
||||
| 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
|
||||
| sigstore-private-key | Sigstore private key to use to sign container images | None
|
||||
| sign-passphrase | Passphrase to unlock the Sigstore private key | None
|
||||
| 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.
|
||||
|
@ -46,20 +49,24 @@ 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.
|
||||
|
@ -68,6 +75,7 @@ If the `tags` input does not have image names in the `${registry}/${name}:${tag}
|
|||
|
||||
`digest`: The pushed image digest, as written to the `digestfile`.<br>
|
||||
For example:
|
||||
|
||||
```
|
||||
sha256:66ce924069ec4181725d15aa27f34afbaf082f434f448dc07a42daa3305cdab3
|
||||
```
|
||||
|
@ -144,11 +152,15 @@ 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.
|
||||
|
||||
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).
|
||||
We recommend using `runs-on: ubuntu-24.04` since it has a newer version of Podman.
|
||||
|
||||
If you are on `ubuntu-22.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).
|
||||
|
||||
Signing using Sigstore is only supported on `ubuntu-24.04` runners.
|
||||
|
||||
## 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.
|
||||
|
|
|
@ -33,6 +33,12 @@ inputs:
|
|||
By default, the filename will be determined from the image and tag.
|
||||
The contents of this file are the digest output.
|
||||
required: false
|
||||
sigstore-private-key:
|
||||
description: 'Sigstore private key to use to sign container images'
|
||||
required: false
|
||||
sign-passphrase:
|
||||
description: 'Passphrase to unlock the Sigstore private key'
|
||||
required: false
|
||||
extra-args:
|
||||
description: |
|
||||
Extra args to be passed to podman push.
|
||||
|
|
6
dist/index.js
vendored
6
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
41
package-lock.json
generated
41
package-lock.json
generated
|
@ -758,12 +758,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
|
@ -844,10 +845,11 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
|
@ -1448,10 +1450,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
|
@ -1956,6 +1959,7 @@
|
|||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
|
@ -2192,12 +2196,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"braces": "^3.0.2",
|
||||
"braces": "^3.0.3",
|
||||
"picomatch": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -2844,6 +2849,7 @@
|
|||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
|
@ -3009,9 +3015,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "5.28.3",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz",
|
||||
"integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==",
|
||||
"version": "5.28.5",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.5.tgz",
|
||||
"integrity": "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/busboy": "^2.0.0"
|
||||
},
|
||||
|
|
|
@ -52,6 +52,18 @@ export enum Inputs {
|
|||
* Default: None.
|
||||
*/
|
||||
USERNAME = "username",
|
||||
/**
|
||||
* Sigstore private key to use to sign container images
|
||||
* Required: false
|
||||
* Default: None.
|
||||
*/
|
||||
SIGSTORE_PRIVATE_KEY = "sigstore-private-key",
|
||||
/**
|
||||
* Passphrase to unlock the Sigstore private key
|
||||
* Required: false
|
||||
* Default: None.
|
||||
*/
|
||||
SIGN_PASSPHRASE = "sign-passphrase",
|
||||
}
|
||||
|
||||
export enum Outputs {
|
||||
|
|
52
src/index.ts
52
src/index.ts
|
@ -209,6 +209,33 @@ async function run(): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
const sigstorePrivateKey = core.getInput(Inputs.SIGSTORE_PRIVATE_KEY);
|
||||
const sigstorePrivateKeyFile = path.join(process.env.RUNNER_TEMP || "", "sigstore_private_key");
|
||||
if (sigstorePrivateKey) {
|
||||
// Write sigstore private key to a temporary file in $RUNNER_TEMP that
|
||||
// will be removed after the image is pushed.
|
||||
try {
|
||||
await fs.promises.writeFile(sigstorePrivateKeyFile, sigstorePrivateKey);
|
||||
}
|
||||
catch (err) {
|
||||
throw new Error(`Could not write sigstore private key to temporary file `
|
||||
+ `"${sigstorePrivateKeyFile}": ${err}`);
|
||||
}
|
||||
}
|
||||
const signPassphrase = core.getInput(Inputs.SIGN_PASSPHRASE);
|
||||
const signPassphraseFile = path.join(process.env.RUNNER_TEMP || "", "sign_passphrase");
|
||||
if (signPassphrase || sigstorePrivateKey) {
|
||||
// Write passphrase (empty string if not provided) to a temporary file
|
||||
// in $RUNNER_TEMP that will be removed after the image is pushed.
|
||||
try {
|
||||
await fs.promises.writeFile(signPassphraseFile, signPassphrase || "");
|
||||
}
|
||||
catch (err) {
|
||||
throw new Error(`Could not write sign passphrase to temporary file `
|
||||
+ `"${signPassphraseFile}": ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
let pushMsg = `⏳ Pushing "${sourceImages.join(", ")}" to "${destinationImages.join(", ")}" respectively`;
|
||||
if (username) {
|
||||
pushMsg += ` as "${username}"`;
|
||||
|
@ -269,11 +296,36 @@ async function run(): Promise<void> {
|
|||
args.push(`--creds=${creds}`);
|
||||
}
|
||||
|
||||
if (sigstorePrivateKey) {
|
||||
args.push("--sign-by-sigstore-private-key");
|
||||
args.push(sigstorePrivateKeyFile);
|
||||
}
|
||||
|
||||
if (signPassphrase || sigstorePrivateKey) {
|
||||
args.push("--sign-passphrase-file");
|
||||
args.push(signPassphraseFile);
|
||||
}
|
||||
|
||||
await execute(await getPodmanPath(), args);
|
||||
core.info(`✅ Successfully pushed "${sourceImages[i]}" to "${destinationImages[i]}"`);
|
||||
|
||||
registryPathList.push(destinationImages[i]);
|
||||
|
||||
try {
|
||||
await fs.promises.unlink(sigstorePrivateKeyFile);
|
||||
}
|
||||
catch (err) {
|
||||
core.warning(`Failed to remove temporary file used to store sigstore private key `
|
||||
+ `"${sigstorePrivateKeyFile}": ${err}`);
|
||||
}
|
||||
try {
|
||||
await fs.promises.unlink(signPassphraseFile);
|
||||
}
|
||||
catch (err) {
|
||||
core.warning(`Failed to remove temporary file used to store sign passphrase `
|
||||
+ `"${signPassphraseFile}": ${err}`);
|
||||
}
|
||||
|
||||
try {
|
||||
const digest = (await fs.promises.readFile(digestFile)).toString();
|
||||
core.info(digest);
|
||||
|
|
Loading…
Add table
Reference in a new issue