Skip to content

Commit

Permalink
fix(publish): Publish candidate to hackage (#21)
Browse files Browse the repository at this point in the history
* fix(publish): Publish candidate to hackage

* delete unnecessary code in prepare

* update tests

* improve logging in prepare step

* sent new file to form in postReleaseCandidate

* new publish tests

* fix linter warnings

* avoid File usage for request

* use axios automate deserialization
  • Loading branch information
dalejo96 authored Mar 12, 2024
1 parent 6887e06 commit 3e7bdaa
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 35 deletions.
11 changes: 2 additions & 9 deletions src/prepare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,10 @@ export const prepare = async (
const realCwd = cwd ?? process.cwd();
logger.log("Current working directory: ", realCwd);
const cabalFileName = cabalFile ?? lookupCabalFilename(realCwd, logger);
const { version } = nextRelease ?? {};

logger.log("Checking new version");
if (!version) {
throw new Error("Could not determine the version from semantic release. Check the plugin configuration");
}

logger.log("Checking cabal file");
const { version } = nextRelease;
const fullCabalPath = resolve(realCwd, cabalFileName);
const fullVersion = `${versionPrefix}${version}`;
logger.log("Reading .cabal file", fullCabalPath);
logger.log("Reading .cabal file: ", fullCabalPath);
await readAndWriteNewCabal(fullCabalPath, fullVersion);
logger.log("Writing new version %s to `%s`", version, fullCabalPath);

Expand Down
41 changes: 21 additions & 20 deletions src/publish.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,47 @@
import axios from "axios";
import { BaseContext } from "semantic-release";
import { PublishContext } from "semantic-release";

import { PluginConfig } from "./types/pluginConfig";
import { runExecCommand } from "./utils/exec";

export const HACKAGE_PACKAGES_URL = "https://hackage.haskell.org/package";
export const CANDIDATES_PATH = "candidates/";
import fs from "fs";

export const HACKAGE_CANDIDATES_URL = "https://hackage.haskell.org/packages/candidates";

export const postReleaseCandidate = async (
sdistPath: string,
packageName: string,
hackageToken?: string,
): Promise<number | undefined> => {
const url = `${HACKAGE_PACKAGES_URL}/${packageName}/${CANDIDATES_PATH}`;
try {
const headers = {
Accept: "text/plain",
Authorization: `X-ApiKey ${hackageToken}`,
"Content-Type": "multipart/form-data",
};

const formData = new FormData();
formData.append("package", sdistPath);

const req = await axios.post(url, formData, { headers });
const req = await axios.post(HACKAGE_CANDIDATES_URL, { package: fs.createReadStream(sdistPath) }, { headers });

return req.status;
} catch (e: unknown) {
throw e instanceof Error ? new Error(`You do not have access to POST a file to ${url}, ${e.message}`) : e;
throw e instanceof Error
? new Error(`You do not have access to POST a file to ${HACKAGE_CANDIDATES_URL} , ${e.message}`)
: e;
}
};

export const publish = async ({ packageName }: PluginConfig, { logger }: BaseContext): Promise<void> => {
logger.log("Getting sdist path");
const { error, output } = await runExecCommand(`ls dist-newstyle/sdist/${packageName}-*.tar.gz`);

if (error) {
logger.error(error);
throw new Error(error);
}
export const publish = async (
{ packageName, versionPrefix }: PluginConfig,
{ logger, nextRelease, cwd }: PublishContext,
): Promise<void> => {
const realCwd = cwd ?? process.cwd();
logger.log("Current working directory: ", realCwd);
const { version } = nextRelease;
logger.log("Getting sdist path with version: ", version);
const filename = `${packageName}-${versionPrefix}${version}.tar.gz`;
const sdistPath = `${realCwd}/dist-newstyle/sdist/${filename}`;
logger.log("Uploading sdist: ", sdistPath);

logger.log("Post release candidate in hackage");
const status = await postReleaseCandidate(output.trim(), packageName, process.env.HACKAGE_TOKEN);
const status = await postReleaseCandidate(sdistPath, process.env.HACKAGE_TOKEN);

if (status !== 200) {
throw new Error(`Cannot post release candidate now, status: ${status}`);
Expand Down
Empty file.
12 changes: 6 additions & 6 deletions test/unit/publish.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,28 @@ import { expect } from "@assertive-ts/core";
import axios from "axios";
import sinon from "sinon";

import { CANDIDATES_PATH, HACKAGE_PACKAGES_URL, postReleaseCandidate } from "../../src/publish";
import { HACKAGE_CANDIDATES_URL, postReleaseCandidate } from "../../src/publish";

const sdistPath = "sdist/path";
const packageName = "my-hackage-package";
const filename = "my-package-1.0.0.tar.gz";
const sdistPath = `${process.cwd()}/test/fixtures/${filename}`;
const hackageToken = "my-fake-token";

describe("postReleaseCandidate", () => {
it("returns the status code when request is successful", async () => {
const axiosPostStub = sinon.stub(axios, "post").resolves({ status: 200 });

const statusCode = await postReleaseCandidate(sdistPath, packageName, hackageToken);
const statusCode = await postReleaseCandidate(sdistPath, hackageToken);

expect(statusCode).toBeEqual(200);
expect(axiosPostStub.calledOnce).toBeTruthy();
expect(axiosPostStub.firstCall.args[0]).toBeEqual(`${HACKAGE_PACKAGES_URL}/${packageName}/${CANDIDATES_PATH}`);
expect(axiosPostStub.firstCall.args[0]).toBeEqual(HACKAGE_CANDIDATES_URL);
});

it("throws an error on unsuccessful request", async () => {
const errorMsg = "Error message from server";
const axiosPostStub = sinon.stub(axios, "post").rejects({ message: errorMsg });

const request = postReleaseCandidate(sdistPath, packageName, hackageToken);
const request = postReleaseCandidate(sdistPath, hackageToken);

await expect(request).toBeRejected();
expect(axiosPostStub.calledOnce).toBeTruthy();
Expand Down

0 comments on commit 3e7bdaa

Please sign in to comment.