This commit is contained in:
Timothée Ravier 2025-02-14 10:19:29 +00:00 committed by GitHub
commit f3368ef416
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 113 additions and 24 deletions

View file

@ -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.

View file

@ -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

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

41
package-lock.json generated
View file

@ -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"
},

View file

@ -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 {

View file

@ -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);