Software title page > View YAML: Simplify copy (#40353)
For the following quick win: - https://github.com/fleetdm/fleet/issues/40354
This commit is contained in:
+8
@@ -17,6 +17,7 @@ import FileProgressModal from "components/FileProgressModal";
|
||||
import PremiumFeatureMessage from "components/PremiumFeatureMessage";
|
||||
import Spinner from "components/Spinner";
|
||||
import DataError from "components/DataError";
|
||||
import InfoBanner from "components/InfoBanner";
|
||||
import CategoriesEndUserExperienceModal from "pages/SoftwarePage/components/modals/CategoriesEndUserExperienceModal";
|
||||
|
||||
import PackageForm from "pages/SoftwarePage/components/forms/PackageForm";
|
||||
@@ -170,6 +171,13 @@ const SoftwareCustomPackage = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{gitOpsModeEnabled && (
|
||||
<InfoBanner color="grey" borderRadius="medium">
|
||||
Add custom packages in GitOps mode so Fleet can host your software.
|
||||
After adding, copy its SHA-256 hash into your YAML so the next
|
||||
GitOps workflow doesn't delete it.
|
||||
</InfoBanner>
|
||||
)}
|
||||
<PackageForm
|
||||
labels={labels || []}
|
||||
showSchemaButton={!isSidePanelOpen}
|
||||
|
||||
@@ -3,3 +3,7 @@
|
||||
margin-top: $pad-xxxlarge;
|
||||
}
|
||||
}
|
||||
|
||||
.info-banner {
|
||||
margin-bottom: $pad-xlarge;
|
||||
}
|
||||
+18
-176
@@ -1,24 +1,19 @@
|
||||
import React, { useContext } from "react";
|
||||
|
||||
import { AppContext } from "context/app";
|
||||
import { NotificationContext } from "context/notification";
|
||||
|
||||
import { LEARN_MORE_ABOUT_BASE_LINK } from "utilities/constants";
|
||||
import { getExtensionFromFileName } from "utilities/file/fileUtils";
|
||||
import FileSaver from "file-saver";
|
||||
import { ISoftwarePackage } from "interfaces/software";
|
||||
import softwareAPI from "services/entities/software";
|
||||
|
||||
import Modal from "components/Modal";
|
||||
import Button from "components/buttons/Button";
|
||||
import InfoBanner from "components/InfoBanner";
|
||||
import CustomLink from "components/CustomLink";
|
||||
// @ts-ignore
|
||||
import InputField from "components/forms/fields/InputField";
|
||||
import Editor from "components/Editor";
|
||||
|
||||
import { hyphenateString } from "utilities/strings/stringUtils";
|
||||
import { createPackageYaml, renderDownloadFilesText } from "./helpers";
|
||||
import createPackageYaml from "./helpers";
|
||||
|
||||
const baseClass = "view-yaml-modal";
|
||||
|
||||
@@ -34,183 +29,39 @@ interface IViewYamlModalProps {
|
||||
isIosOrIpadosApp?: boolean;
|
||||
}
|
||||
|
||||
interface HandleDownloadParams {
|
||||
evt: React.MouseEvent;
|
||||
content?: string;
|
||||
downloadUrl?: string;
|
||||
filename: string;
|
||||
filetype: string;
|
||||
errorMsg: string;
|
||||
}
|
||||
|
||||
const ViewYamlModal = ({
|
||||
softwareTitleName,
|
||||
softwareTitleId: softwareId,
|
||||
teamId,
|
||||
iconUrl,
|
||||
displayName,
|
||||
softwarePackage,
|
||||
onExit,
|
||||
isScriptPackage = false,
|
||||
isIosOrIpadosApp = false,
|
||||
}: IViewYamlModalProps) => {
|
||||
const { renderFlash } = useContext(NotificationContext);
|
||||
const { config } = useContext(AppContext);
|
||||
const repositoryUrl = config?.gitops?.repository_url;
|
||||
const { name, version, url, hash_sha256: sha256 } = softwarePackage;
|
||||
|
||||
// Script packages (.sh and .ps1) should not expose install_script,
|
||||
// post_install_script, uninstall_script, or pre_install_query fields
|
||||
const preInstallQuery = !isScriptPackage
|
||||
? softwarePackage.pre_install_query
|
||||
: undefined;
|
||||
const installScript = !isScriptPackage
|
||||
? softwarePackage.install_script
|
||||
: undefined;
|
||||
const postInstallScript = !isScriptPackage
|
||||
? softwarePackage.post_install_script
|
||||
: undefined;
|
||||
const uninstallScript = !isScriptPackage
|
||||
? softwarePackage.uninstall_script
|
||||
: undefined;
|
||||
|
||||
const packageYaml = createPackageYaml({
|
||||
softwareTitle: softwareTitleName,
|
||||
packageName: name,
|
||||
version,
|
||||
url,
|
||||
sha256,
|
||||
preInstallQuery,
|
||||
installScript,
|
||||
postInstallScript,
|
||||
uninstallScript,
|
||||
iconUrl: iconUrl || null,
|
||||
displayName,
|
||||
isScriptPackage,
|
||||
});
|
||||
|
||||
// Generic download handler
|
||||
const handleDownload = async ({
|
||||
evt,
|
||||
content,
|
||||
downloadUrl,
|
||||
filename,
|
||||
filetype,
|
||||
errorMsg,
|
||||
}: HandleDownloadParams) => {
|
||||
evt.preventDefault();
|
||||
|
||||
try {
|
||||
if (content) {
|
||||
const file = new window.File([content], filename, { type: filetype });
|
||||
FileSaver.saveAs(file);
|
||||
} else if (downloadUrl) {
|
||||
const response = await fetch(downloadUrl);
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
const blob = await response.blob();
|
||||
FileSaver.saveAs(blob, filename);
|
||||
} else {
|
||||
throw new Error("No content or URL provided");
|
||||
}
|
||||
} catch (err) {
|
||||
renderFlash("error", errorMsg);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const hyphenatedSoftwareTitle = hyphenateString(softwareTitleName);
|
||||
|
||||
const onDownloadPreInstallQuery = (evt: React.MouseEvent) => {
|
||||
const softwareExtension = getExtensionFromFileName(name);
|
||||
const preInstallQueryContent = `- name: "[Pre-install software] ${softwareTitleName} (${softwareExtension})"\n query: ${preInstallQuery}`;
|
||||
|
||||
handleDownload({
|
||||
evt,
|
||||
content: preInstallQueryContent,
|
||||
filename: `pre-install-query-${hyphenatedSoftwareTitle}.yml`,
|
||||
filetype: "text/yml",
|
||||
errorMsg:
|
||||
"Your pre-install query could not be downloaded. Please create YAML file (.yml) manually.",
|
||||
});
|
||||
};
|
||||
|
||||
const onDownloadPostInstallScript = (evt: React.MouseEvent) => {
|
||||
handleDownload({
|
||||
evt,
|
||||
content: postInstallScript,
|
||||
filename: `post-install-${hyphenatedSoftwareTitle}.sh`,
|
||||
filetype: "text/sh",
|
||||
errorMsg:
|
||||
"Your post-install script could not be downloaded. Please create script file (.sh) manually.",
|
||||
});
|
||||
};
|
||||
|
||||
const onDownloadInstallScript = (evt: React.MouseEvent) => {
|
||||
handleDownload({
|
||||
evt,
|
||||
content: installScript,
|
||||
filename: `install-${hyphenatedSoftwareTitle}.sh`,
|
||||
filetype: "text/sh",
|
||||
errorMsg:
|
||||
"Your install script could not be downloaded. Please create script file (.sh) manually.",
|
||||
});
|
||||
};
|
||||
|
||||
const onDownloadUninstallScript = (evt: React.MouseEvent) => {
|
||||
handleDownload({
|
||||
evt,
|
||||
content: uninstallScript,
|
||||
filename: `uninstall-${hyphenatedSoftwareTitle}.sh`,
|
||||
filetype: "text/sh",
|
||||
errorMsg:
|
||||
"Your uninstall script could not be downloaded. Please create script file (.sh) manually.",
|
||||
});
|
||||
};
|
||||
|
||||
const onDownloadIcon = async (evt: React.MouseEvent) => {
|
||||
evt.preventDefault();
|
||||
|
||||
try {
|
||||
// Get icon blob + create filename
|
||||
const response = await softwareAPI.getSoftwareIcon(softwareId, teamId);
|
||||
// Different from icon's original filename as we are suggesting a standard name used in YAML
|
||||
const filename = `${hyphenatedSoftwareTitle}-icon.png`;
|
||||
|
||||
// Save the file
|
||||
FileSaver.saveAs(response.data, filename);
|
||||
} catch (err) {
|
||||
renderFlash(
|
||||
"error",
|
||||
"Your icon could not be downloaded. Please download the image manually."
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal className={baseClass} title="YAML" onExit={onExit}>
|
||||
<>
|
||||
<InfoBanner className={`${baseClass}__info-banner`}>
|
||||
<p>
|
||||
To complete your GitOps configuration, follow the instructions
|
||||
below. If the YAML is not added, new installers will be deleted on
|
||||
the next GitOps run, and edited installers will cause the GitOps run
|
||||
to fail.
|
||||
<CustomLink
|
||||
url={`${LEARN_MORE_ABOUT_BASE_LINK}/yaml-packages`}
|
||||
text="How to use YAML"
|
||||
newTab
|
||||
multiline
|
||||
variant="banner-link"
|
||||
/>
|
||||
</p>
|
||||
</InfoBanner>
|
||||
{repositoryUrl && (
|
||||
<p>
|
||||
First, create the YAML file below and save it to your{" "}
|
||||
<CustomLink url={repositoryUrl} text="repository" newTab />.
|
||||
Manage in <CustomLink url={repositoryUrl} text="YAML" newTab />.
|
||||
</p>
|
||||
)}
|
||||
<p>Make sure you reference the package YAML from your fleet YAML.</p>
|
||||
<div className={`${baseClass}__form-fields`}>
|
||||
<InputField
|
||||
enableCopy
|
||||
@@ -219,32 +70,23 @@ const ViewYamlModal = ({
|
||||
label="Filename"
|
||||
value={`${hyphenatedSoftwareTitle}.package.yml`}
|
||||
/>
|
||||
<Editor label="Contents" value={packageYaml} enableCopy />
|
||||
<Editor
|
||||
label="Contents"
|
||||
value={packageYaml}
|
||||
enableCopy
|
||||
helpText={
|
||||
<>
|
||||
If you added advanced options, learn how to{" "}
|
||||
<CustomLink
|
||||
url={`${LEARN_MORE_ABOUT_BASE_LINK}/yaml-packages`}
|
||||
text="add them to your YAML"
|
||||
newTab
|
||||
/>
|
||||
.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<p>
|
||||
{renderDownloadFilesText({
|
||||
preInstallQuery,
|
||||
installScript,
|
||||
postInstallScript,
|
||||
uninstallScript,
|
||||
iconUrl,
|
||||
onClickPreInstallQuery: preInstallQuery
|
||||
? onDownloadPreInstallQuery
|
||||
: undefined,
|
||||
onClickInstallScript: installScript
|
||||
? onDownloadInstallScript
|
||||
: undefined,
|
||||
onClickPostInstallScript: postInstallScript
|
||||
? onDownloadPostInstallScript
|
||||
: undefined,
|
||||
onClickUninstallScript: uninstallScript
|
||||
? onDownloadUninstallScript
|
||||
: undefined,
|
||||
onClickIcon: iconUrl ? onDownloadIcon : undefined,
|
||||
hasAdvancedOptionsAvailable: !isScriptPackage && !isIosOrIpadosApp,
|
||||
isScriptPackage,
|
||||
})}
|
||||
</p>
|
||||
<div className="modal-cta-wrap">
|
||||
<Button onClick={onExit}>Done</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
.view-yaml-modal {
|
||||
overflow-wrap: anywhere; // Prevent long overflow
|
||||
|
||||
.info-banner__info {
|
||||
p {
|
||||
margin: 0; // Undo weird top margin from info-banner component
|
||||
}
|
||||
.editor__label {
|
||||
color: $core-fleet-black;
|
||||
}
|
||||
|
||||
&__form-fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $pad-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-197
@@ -1,8 +1,6 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { createMockSoftwarePackage } from "__mocks__/softwareMock";
|
||||
import { noop } from "lodash";
|
||||
|
||||
import { createPackageYaml, renderDownloadFilesText } from "./helpers";
|
||||
import createPackageYaml from "./helpers";
|
||||
|
||||
describe("createPackageYaml", () => {
|
||||
const {
|
||||
@@ -201,197 +199,3 @@ describe("createPackageYaml", () => {
|
||||
path: ./icons/falcon-sensor-test-package-icon.png`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("renderYamlHelperText", () => {
|
||||
const {
|
||||
pre_install_query: preInstallQuery,
|
||||
install_script: installScript,
|
||||
post_install_script: postInstallScript,
|
||||
uninstall_script: uninstallScript,
|
||||
} = createMockSoftwarePackage();
|
||||
|
||||
it("renders nothing if no scripts/queries are present", () => {
|
||||
// Empty to simulate 'no items'
|
||||
const { container } = render(renderDownloadFilesText({}));
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it("renders correctly with one item", () => {
|
||||
// Only install_script present
|
||||
render(
|
||||
renderDownloadFilesText({ installScript, onClickInstallScript: noop })
|
||||
);
|
||||
expect(
|
||||
screen.getByRole("button", { name: "install script" })
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText((content) =>
|
||||
content.includes("add it to your repository using the path above.")
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByText("and")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders correctly with two items", () => {
|
||||
const { container } = render(
|
||||
renderDownloadFilesText({
|
||||
installScript,
|
||||
uninstallScript,
|
||||
onClickInstallScript: noop,
|
||||
onClickUninstallScript: noop,
|
||||
})
|
||||
);
|
||||
expect(
|
||||
screen.getByRole("button", { name: "install script" })
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("button", { name: "uninstall script" })
|
||||
).toBeInTheDocument();
|
||||
|
||||
// In "Next," and "Advanced options," only
|
||||
const text = container.textContent ?? "";
|
||||
const commaCount = (text.match(/,/g) || []).length;
|
||||
expect(commaCount).toBe(2);
|
||||
|
||||
// No oxford comma for two items
|
||||
expect(
|
||||
screen.queryByText((content) => content.includes(", and"))
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText((content) =>
|
||||
content.includes("add them to your repository using the paths above.")
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders correctly with all items", () => {
|
||||
// All present (default)
|
||||
const { container } = render(
|
||||
renderDownloadFilesText({
|
||||
preInstallQuery,
|
||||
installScript,
|
||||
uninstallScript,
|
||||
postInstallScript,
|
||||
onClickPreInstallQuery: noop,
|
||||
onClickInstallScript: noop,
|
||||
onClickUninstallScript: noop,
|
||||
onClickPostInstallScript: noop,
|
||||
})
|
||||
);
|
||||
expect(
|
||||
screen.getByRole("button", { name: "pre-install query" })
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("button", { name: "install script" })
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("button", { name: "post-install script" })
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("button", { name: "uninstall script" })
|
||||
).toBeInTheDocument();
|
||||
|
||||
// In "Next," "Advanced options," and 3 more commas
|
||||
const text = container.textContent ?? "";
|
||||
const commaCount = (text.match(/,/g) || []).length;
|
||||
expect(commaCount).toBe(5);
|
||||
|
||||
// Oxford comma for four items
|
||||
expect(
|
||||
screen.queryByText((content) => content.includes(", and"))
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText((content) =>
|
||||
content.includes("add them to your repository using the paths above.")
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders comma correctly for three items (with Oxford comma)", () => {
|
||||
// pre_install_query, install_script, uninstall_script present
|
||||
const { container } = render(
|
||||
renderDownloadFilesText({
|
||||
preInstallQuery,
|
||||
installScript,
|
||||
uninstallScript,
|
||||
onClickPreInstallQuery: noop,
|
||||
onClickInstallScript: noop,
|
||||
onClickUninstallScript: noop,
|
||||
})
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByRole("button", { name: "pre-install query" })
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("button", { name: "install script" })
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("button", { name: "uninstall script" })
|
||||
).toBeInTheDocument();
|
||||
|
||||
// In "Next," "Advanced options," and 2 more commas
|
||||
const text = container.textContent ?? "";
|
||||
const commaCount = (text.match(/,/g) || []).length;
|
||||
expect(commaCount).toBe(4);
|
||||
|
||||
// Oxford comma for three items
|
||||
expect(
|
||||
screen.getByText((content) => content.includes(", and"))
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText((content) =>
|
||||
content.includes("add them to your repository using the paths above.")
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render advanced options help text when hasAdvancedOptionsAvailable is false", () => {
|
||||
render(
|
||||
renderDownloadFilesText({
|
||||
installScript: "echo install",
|
||||
onClickInstallScript: noop,
|
||||
hasAdvancedOptionsAvailable: false,
|
||||
})
|
||||
);
|
||||
|
||||
// Should not show the "Advanced options" instructional text
|
||||
expect(screen.queryByText(/If you edited/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/Advanced options/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render script-only fields for script packages", () => {
|
||||
// Script packages should not show download links for install_script,
|
||||
// post_install_script, uninstall_script, or pre_install_query
|
||||
const { container } = render(
|
||||
renderDownloadFilesText({
|
||||
preInstallQuery,
|
||||
installScript,
|
||||
postInstallScript,
|
||||
uninstallScript,
|
||||
onClickPreInstallQuery: noop,
|
||||
onClickInstallScript: noop,
|
||||
onClickPostInstallScript: noop,
|
||||
onClickUninstallScript: noop,
|
||||
isScriptPackage: true,
|
||||
})
|
||||
);
|
||||
|
||||
// Should not render any download buttons for script-only fields
|
||||
expect(
|
||||
screen.queryByRole("button", { name: "pre-install query" })
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole("button", { name: "install script" })
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole("button", { name: "post-install script" })
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole("button", { name: "uninstall script" })
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
// Container should be empty since no items should be rendered
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,23 +1,7 @@
|
||||
import React, { MouseEvent } from "react";
|
||||
|
||||
import Button from "components/buttons/Button";
|
||||
import { hyphenateString } from "utilities/strings/stringUtils";
|
||||
|
||||
interface RenderYamlHelperText {
|
||||
installScript?: string;
|
||||
uninstallScript?: string;
|
||||
preInstallQuery?: string;
|
||||
postInstallScript?: string;
|
||||
iconUrl?: string | null;
|
||||
onClickPreInstallQuery?: (evt: MouseEvent) => void;
|
||||
onClickInstallScript?: (evt: MouseEvent) => void;
|
||||
onClickPostInstallScript?: (evt: MouseEvent) => void;
|
||||
onClickUninstallScript?: (evt: MouseEvent) => void;
|
||||
onClickIcon?: (evt: MouseEvent) => void;
|
||||
hasAdvancedOptionsAvailable?: boolean;
|
||||
isScriptPackage?: boolean;
|
||||
}
|
||||
|
||||
// Helper to join items with commas and Oxford comma before "and"
|
||||
const joinWithCommasAnd = (
|
||||
elements: { key: string; element: JSX.Element }[]
|
||||
@@ -39,106 +23,6 @@ const joinWithCommasAnd = (
|
||||
});
|
||||
};
|
||||
|
||||
export const renderDownloadFilesText = ({
|
||||
preInstallQuery,
|
||||
installScript,
|
||||
postInstallScript,
|
||||
uninstallScript,
|
||||
iconUrl,
|
||||
onClickPreInstallQuery,
|
||||
onClickInstallScript,
|
||||
onClickPostInstallScript,
|
||||
onClickUninstallScript,
|
||||
onClickIcon,
|
||||
hasAdvancedOptionsAvailable = true,
|
||||
isScriptPackage = false,
|
||||
}: RenderYamlHelperText): JSX.Element => {
|
||||
const items: { key: string; element: JSX.Element }[] = [];
|
||||
|
||||
// Script packages (.sh and .ps1) should not expose install_script,
|
||||
// post_install_script, uninstall_script, or pre_install_query fields
|
||||
if (!isScriptPackage && preInstallQuery) {
|
||||
items.push({
|
||||
key: "pre-install-query",
|
||||
element: (
|
||||
<Button key="pre" variant="text-link" onClick={onClickPreInstallQuery}>
|
||||
pre-install query
|
||||
</Button>
|
||||
),
|
||||
});
|
||||
}
|
||||
if (!isScriptPackage && installScript) {
|
||||
items.push({
|
||||
key: "install-script",
|
||||
element: (
|
||||
<Button
|
||||
key="install"
|
||||
variant="text-link"
|
||||
onClick={onClickInstallScript}
|
||||
>
|
||||
install script
|
||||
</Button>
|
||||
),
|
||||
});
|
||||
}
|
||||
if (!isScriptPackage && uninstallScript) {
|
||||
items.push({
|
||||
key: "uninstall-script",
|
||||
element: (
|
||||
<Button
|
||||
key="uninstall"
|
||||
variant="text-link"
|
||||
onClick={onClickUninstallScript}
|
||||
>
|
||||
uninstall script
|
||||
</Button>
|
||||
),
|
||||
});
|
||||
}
|
||||
if (!isScriptPackage && postInstallScript) {
|
||||
items.push({
|
||||
key: "post-install-script",
|
||||
element: (
|
||||
<Button
|
||||
key="post"
|
||||
variant="text-link"
|
||||
onClick={onClickPostInstallScript}
|
||||
>
|
||||
post-install script
|
||||
</Button>
|
||||
),
|
||||
});
|
||||
}
|
||||
if (iconUrl) {
|
||||
items.push({
|
||||
key: "icon-url",
|
||||
element: (
|
||||
<Button key="post" variant="text-link" onClick={onClickIcon}>
|
||||
icon
|
||||
</Button>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (items.length === 0) return <></>;
|
||||
|
||||
return (
|
||||
<>
|
||||
Next, download your {joinWithCommasAnd(items)} and add{" "}
|
||||
{items.length === 1 ? "it" : "them"} to your repository using the{" "}
|
||||
{items.length === 1 ? "path" : "paths"} above.
|
||||
{hasAdvancedOptionsAvailable && (
|
||||
<>
|
||||
{" "}
|
||||
If you edited <b>Advanced options</b>, download and replace the{" "}
|
||||
{items.length === 1 ? "file" : "files"} in your repository with the
|
||||
updated {items.length === 1 ? "one" : "ones"}.
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface CreatePackageYamlParams {
|
||||
softwareTitle: string;
|
||||
packageName: string;
|
||||
@@ -154,7 +38,7 @@ interface CreatePackageYamlParams {
|
||||
isScriptPackage?: boolean;
|
||||
}
|
||||
|
||||
export const createPackageYaml = ({
|
||||
const createPackageYaml = ({
|
||||
softwareTitle,
|
||||
packageName,
|
||||
version,
|
||||
@@ -224,3 +108,5 @@ export const createPackageYaml = ({
|
||||
|
||||
return yaml.trim();
|
||||
};
|
||||
|
||||
export default createPackageYaml;
|
||||
|
||||
Reference in New Issue
Block a user