From 621ccc06f9b3898ab7edfa4a42e2b7a1fe21c468 Mon Sep 17 00:00:00 2001
From: smac89 <8305511+smac89@users.noreply.github.com>
Date: Fri, 17 Nov 2023 21:46:52 -0600
Subject: [PATCH] rename some files and add tag push support
---
README.md | 79 +++++--
src/functions/index.ts | 224 ++++++++++++--------
src/functions/types.ts | 34 +++
src/index.ts | 61 +-----
src/main.ts | 94 ++++----
src/util.ts | 35 +++
tests/functions.test.ts | 20 +-
tests/{action.test.ts => main.test.ts} | 0
tests/test-setup.ts | 2 +
tests/{preferences.test.ts => util.test.ts} | 20 +-
10 files changed, 357 insertions(+), 212 deletions(-)
create mode 100644 src/functions/types.ts
create mode 100644 src/util.ts
rename tests/{action.test.ts => main.test.ts} (100%)
rename tests/{preferences.test.ts => util.test.ts} (54%)
diff --git a/README.md b/README.md
index 1de9de5..824ddae 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,22 @@
# Actions Tagger
-:speedboat: Keep your action versions up-to-date by automatically promoting a major tag (and optionally, a `latest` tag) each time a release is created.
+:speedboat: Keep your action versions up-to-date by automatically promoting a
+major tag (and optionally, a `latest` tag) each time a release is created.
# Rationale
-According to the github actions [versioning guide](https://github.com/actions/toolkit/blob/master/docs/action-versioning.md#versioning), actions publishers should have a major tag (`v1`, `v2`, etc) which points to the latest version of any minor/patch release of their action, for ease of use by the others.
+According to the github actions
+[versioning guide](https://github.com/actions/toolkit/blob/master/docs/action-versioning.md#versioning),
+actions publishers should have a major tag (`v1`, `v2`, etc) which points to the
+latest version of any minor/patch release of their action, for ease of use by
+the others.
-I found this process quite tedious, and repetitive which is why this action exists. If you have published an action and would like to have your action follow the same versioning structure as many others in the [marketplace](https://github.com/marketplace?type=actions), then simply create a release workflow that includes this action. See the [_usage_ example](#sample-usage).
+I found this process quite tedious, and repetitive which is why this action
+exists. If you have published an action and would like to have your action
+follow the same versioning structure as many others in the
+[marketplace](https://github.com/marketplace?type=actions), then simply create a
+release workflow that includes this action. See the
+[_usage_ example](#sample-usage).
---
@@ -16,7 +26,8 @@ I found this process quite tedious, and repetitive which is why this action exis
### `publish_latest_tag`
-Indicates to the action whether or not to create/update a tag called `latest` pointing to the latest release. **Default: `"false"`**.
+Indicates to the action whether or not to create/update a tag called `latest`
+pointing to the latest release. **Default: `"false"`**.
### `prefer_branch_releases`
@@ -24,7 +35,8 @@ Do you prefer creating `vN` branches or `vN` tags? **Default: `"false"`**
### `token`
-A github token used for creating an octoclient for making API calls. **Default: `${{ github.token }}`**.
+A github token used for creating an octoclient for making API calls. **Default:
+`${{ github.token }}`**.
## Outputs
@@ -37,19 +49,23 @@ The version of the branch/tag that was published/updated.
Was _latest_ also published?
### `ref_name`
+
**Deprecated in v3:** _Use [`tag`](#tag)_
# Env Inputs
### `GITHUB_TOKEN`
-**Deprecated in v3:** _If a non-default PAT (Personal Access Token) is needed, use [`token`](#token) instead._
+**Deprecated in v3:** _If a non-default PAT (Personal Access Token) is needed,
+use [`token`](#token) instead._
# Debug Logging
-This action supports [debug logging](https://docs.github.com/en/actions/managing-workflow-runs/enabling-debug-logging#enabling-step-debug-logging). When enabled, it will dump the output of the
-api call for creating the tags/branches.
-This is useful for testing and should be included when reporting bugs.
+This action supports
+[debug logging](https://docs.github.com/en/actions/managing-workflow-runs/enabling-debug-logging#enabling-step-debug-logging).
+When enabled, it will dump the output of the api call for creating the
+tags/branches. This is useful for testing and should be included when reporting
+bugs.
# Sample Usage
@@ -60,30 +76,63 @@ name: Keep the versions up-to-date
on:
release:
- types: [published, edited]
+ types:
+ - published
+ - edited
+ push: #(1)
+ tags-ignore:
+ - 'v[0-9]+'
+ - 'latest'
+ branches-ignore:
+ - '**'
+ paths-ignore:
+ - '**'
jobs:
actions-tagger:
runs-on: windows-latest
- permissions: # (1)
+ permissions: # (2)
contents: write
steps:
- uses: Actions-R-Us/actions-tagger@latest
with:
publish_latest_tag: true
```
-_Note this action is able to detect if it is being run in a **release** context, and if not it will notify you and exit gracefully._
-
---
### Notes
-1. The [`permissions`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions) option is only required if the workflow permission for the given repository is set to readonly. `readonly` permission renders the main purpose of this action useless because it will be unable to create tags. Using the `contents: write` scope allows this action to once again gain the ability to create/update tags. For more details on changing the workflow permissions for a given repository, see [Configuring the default `GITHUB_TOKEN` permissions](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#configuring-the-default-github_token-permissions). For more details on the various available scopes that can be configured for the `GITHUB_TOKEN`, see [`permissions`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions).
- It is also important to note that when modifying one scope of the [permission of `GITHUB_TOKEN`](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token), all other unspecified scopes are set to _no access_ with the exception of the `metadata` scope, which is set to `read`. See [Modifying the permissions for the `GITHUB_TOKEN`](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token) for more details. This shouldn't be a concern for this action, because it only exclusively deals with the contents of the given repository.
+1. Add the push configuration if you want this action to also run when a new tag or branch is created.
+
+ **An event will [not be created](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push) when more than three tags are pushed at once.**
+
+ If you want to track branches, swap the filters listed under `branches-ignore`
+ and `tags-ignore`. At all times, leave the filter for `paths-ignore` as is.
+2. The
+ [`permissions`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions)
+ option is only required if the workflow permission for the given repository
+ is set to readonly. `readonly` permission renders the main purpose of this
+ action useless because it will be unable to create tags. Using the
+ `contents: write` scope allows this action to once again gain the ability to
+ create/update tags. For more details on changing the workflow permissions for
+ a given repository, see
+ [Configuring the default `GITHUB_TOKEN` permissions](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#configuring-the-default-github_token-permissions).
+ For more details on the various available scopes that can be configured for
+ the `GITHUB_TOKEN`, see
+ [`permissions`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions).
+
+ It is also important to note that when modifying one scope of the
+ [permission of `GITHUB_TOKEN`](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token),
+ all other unspecified scopes are set to _no access_ with the exception of the
+ `metadata` scope, which is set to `read`. See
+ [Modifying the permissions for the `GITHUB_TOKEN`](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token)
+ for more details. This shouldn't be a concern for this action, because it
+ only exclusively deals with the contents of the given repository.
---
# Similar projects
### [EndBug/latest-tag](https://github.com/EndBug/latest-tag)
+
- Creates a `latest` tag
diff --git a/src/functions/index.ts b/src/functions/index.ts
index 284ec3b..8ac60ac 100644
--- a/src/functions/index.ts
+++ b/src/functions/index.ts
@@ -1,25 +1,18 @@
import * as core from '@actions/core';
-import { context, getOctokit } from '@actions/github';
+import { context } from '@actions/github';
import SemVer from 'semver/classes/semver';
import coerce from 'semver/functions/coerce';
import semverGt from 'semver/functions/gt';
import major from 'semver/functions/major';
import semverParse from 'semver/functions/parse';
import valid from 'semver/functions/valid';
-import {
- GraphQlQueryRepository,
- LatestRelease,
- preferences,
- queryAllRefs,
- TaggedRelease,
-} from '@actionstagger';
-
-type GitHub = ReturnType;
+import { preferences, queryAllRefs } from '@actionstagger/util';
+import type { GitHub, GraphQlQueryRepository, TaggedRef, LatestRef } from './types';
namespace Functions {
/**
* Checks if the event that triggered this action was a release
- * See: https://developer.github.com/v3/activity/events/types/#releaseevent
+ * See: https://docs.github.com/en/webhooks/webhook-events-and-payloads#release
*/
function isRelease(): boolean {
return context.eventName === 'release';
@@ -31,8 +24,8 @@ namespace Functions {
*
* For some reason, it is not enough to check if the action is
* prereleased, because even prereleases have the action of "published"
- * See: https://github.community/t5/GitHub-Actions/Release-Prerelease-action-triggers/m-p/42177#M4892
- * See also: https://developer.github.com/v3/activity/events/types/#releaseevent
+ * See: https://github.com/orgs/community/discussions/26281
+ * See also: https://docs.github.com/en/webhooks/webhook-events-and-payloads#release
*/
function isPreRelease(): boolean {
return context.payload.release?.prerelease === true;
@@ -46,6 +39,29 @@ namespace Functions {
return isRelease() && !isPreRelease();
}
+ /**
+ * Checks if the event that triggered this action was a push
+ * See: https://docs.github.com/en/webhooks/webhook-events-and-payloads#push
+ */
+ function isPush(): boolean {
+ return context.eventName === 'push';
+ }
+
+ /**
+ * Check if the push event created a new ref
+ */
+ function isNewRefPush(): boolean {
+ return isPush() && context.payload.created === true;
+ }
+
+ function isBranchPush(): boolean {
+ return isNewRefPush() && context.payload.ref.startsWith(`refs/heads/`);
+ }
+
+ function isTagPush(): boolean {
+ return isNewRefPush() && context.payload.ref.startsWith(`refs/tags/`);
+ }
+
/**
* Creates the given ref for this release
* refName must begin with tags/ or heads/
@@ -91,13 +107,61 @@ namespace Functions {
}
}
+ /**
+ * List all the refs in the repository based on user's preferred ref
+ *
+ * @param github The github client
+ */
+ async function* listAllRefs(github: GitHub) {
+ for (let nextPage: string; true; ) {
+ const { repository }: { repository: GraphQlQueryRepository } =
+ await github.graphql(queryAllRefs, {
+ repoName: context.repo.repo,
+ repoOwner: context.repo.owner,
+ majorRef: `refs/${Functions.getPreferredRef()}/`,
+ pagination: nextPage,
+ });
+
+ for (const { ref } of repository.refs.refsList) {
+ const semverRef = semverParse(ref.name);
+ if (semverRef !== null) {
+ if (core.isDebug()) {
+ core.debug(`checking ${ref.name}`);
+ }
+ yield [semverRef, ref.object.shaId] as const;
+ } else if (core.isDebug()) {
+ core.debug(`ignoring ${ref.name}`);
+ }
+ }
+
+ if (repository.refs.pageInfo.hasNextPage) {
+ nextPage = repository.refs.pageInfo.endCursor;
+ } else {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Get the ref version for the current push
+ *
+ * @returns the ref for this release (if any)
+ */
+ function getPushRefVersion(): SemVer {
+ let refName: string | SemVer = (context.payload.ref as string)?.replace(
+ new RegExp(`^refs/${Functions.getPreferredRef()}/`),
+ ''
+ );
+ return semverParse(refName);
+ }
+
/**
* Get the actual tag version for this release. It also takes into account
* whether or not this is a prerelease
*
* @returns the tag for this release (if any)
*/
- export function releaseTag(): SemVer {
+ function getReleaseTag(): SemVer {
let tagName: string | SemVer = context.payload.release?.tag_name;
if (isPreRelease()) {
tagName = coerce(tagName);
@@ -120,23 +184,51 @@ namespace Functions {
}
/**
- * Checks if the tag version of the release is valid semantic version
+ * Check if this event was a new tag push
+ */
+ export function isRefPush(): boolean {
+ return isBranchPush() || isTagPush();
+ }
+
+ export async function isRefLatestMajor(github: GitHub): Promise {
+ if (Functions.isRefPush()) {
+ const major = majorVersion();
+ const { data: majorRef } = await github.rest.git.getRef({
+ ...context.repo,
+ ref: `${Functions.getPreferredRef()}/v${major}`,
+ });
+ return majorRef?.object.sha === process.env.GITHUB_SHA;
+ }
+ return false;
+ }
+
+ /**
+ * Get the tag version being published via push or release event
+ *
+ * @returns The tag being published
+ */
+ export function getPublishRefVersion(): SemVer {
+ return Functions.isRefPush() ? getPushRefVersion() : getReleaseTag();
+ }
+
+ /**
+ * Checks if the tag version of the pushed tag/release has valid version
*/
- export function isSemVersionedRelease(): boolean {
- return valid(Functions.releaseTag()) !== null;
+ export function isSemVersionedTag(): boolean {
+ return valid(Functions.getPublishRefVersion()) !== null;
}
/**
* Get the major number of the release tag
*/
export function majorVersion(): number {
- return major(Functions.releaseTag());
+ return major(Functions.getPublishRefVersion());
}
/**
* Returns the appropriate ref depending on the input preferences
*/
- export function getPreferredRef(): string {
+ export function getPreferredRef() {
if (preferences.preferBranchRelease) {
return 'heads';
}
@@ -144,64 +236,40 @@ namespace Functions {
}
/**
- * Finds the latest release in the repository as well as the latest release
- * for this release major version. We do this to determine if we should proceed
+ * Finds the latest refs in the repository as well as the latest ref
+ * for this event's major version. We do this to determine if we should proceed
* with promoting the major and latest refs.
*
- * e.g. if the current release which triggered this actions is tagged v3.2.2,
- * but the latest release is v4.0.3, a possible return value may be
+ * e.g. if the current ref which triggered this actions is tagged v3.2.2,
+ * but the latest ref is v4.0.3, a possible return value may be
* {repoLatest: "v4.0.3", majorLatest: "v3.3.0"} (this is not a typo, keep reading).
- * In this case, this release should not trigger an update because the release is
+ * In this case, this event should not trigger an update because it is
* targetting a lower version than the latest v3 (v3.3.0) and we already have a latest version
* which is v4.0.3
*
* @param {GitHub} github The octokit client instance
*/
- export async function findLatestReleases(github: GitHub): Promise {
- const releaseVer = Functions.releaseTag();
- let repoLatest = releaseVer;
- let majorLatest = repoLatest;
-
- const major = Functions.majorVersion();
-
- if (core.isDebug()) {
- core.debug('Found the following releases in this repository:');
- }
-
- for (let nextPage: string; true; ) {
- const { repository }: { repository: GraphQlQueryRepository } =
- await github.graphql(queryAllRefs, {
- repoName: context.repo.repo,
- repoOwner: context.repo.owner,
- majorRef: `refs/${Functions.getPreferredRef()}/`,
- pagination: nextPage,
- });
-
- for (const { ref } of repository.refs.refsList) {
- const semverRef = semverParse(ref.name);
- if (semverRef !== null) {
- if (semverRef.major === major && semverGt(semverRef, majorLatest)) {
- majorLatest = semverRef;
- }
+ export async function findLatestRef(github: GitHub): Promise {
+ let [majorLatest, majorSha] = [
+ Functions.getPublishRefVersion(),
+ process.env.GITHUB_SHA,
+ ];
+ let [repoLatest, repoSha] = [majorLatest, majorSha];
- if (semverGt(semverRef, repoLatest)) {
- repoLatest = semverRef;
- }
-
- if (core.isDebug()) {
- core.debug(ref.name);
- }
- }
+ const major = majorLatest.major;
+ for await (const [semverRef, shaId] of listAllRefs(github)) {
+ if (semverRef.major === major && semverGt(semverRef, majorLatest)) {
+ [majorLatest, majorSha] = [semverRef, shaId];
}
- if (repository.refs.pageInfo.hasNextPage) {
- nextPage = repository.refs.pageInfo.endCursor;
- } else {
- break;
+ if (semverGt(semverRef, repoLatest)) {
+ [repoLatest, repoSha] = [semverRef, shaId];
}
}
-
- return { repoLatest: repoLatest.version, majorLatest: majorLatest.version };
+ return {
+ repoLatest: { name: repoLatest.version, shaId: repoSha },
+ majorLatest: { name: majorLatest.version, shaId: majorSha },
+ };
}
/**
@@ -213,38 +281,22 @@ namespace Functions {
export async function createRequiredRefs(
github: GitHub,
overridePublishLatest?: boolean
- ): Promise {
+ ): Promise {
const mayor = Functions.majorVersion();
const ref = `${Functions.getPreferredRef()}/v${mayor}`;
await createRef(github, ref);
- const publishLatest: boolean =
- overridePublishLatest ?? preferences.publishLatestTag;
+ const publishLatest: boolean = overridePublishLatest ?? preferences.publishLatest;
if (publishLatest) {
// TODO v3: `${getPreferredRef()}/latest`
await createRef(github, 'tags/latest');
}
- return { ref, latest: publishLatest };
- }
-
- /**
- * Sets the output of this action to indicate the version that was published/updated
- * @param refName The tag version
- */
- export function outputTagName(refName: string) {
- core.setOutput('tag', refName);
- // TODO: Deprecate: v3
- core.setOutput('ref_name', refName);
- }
-
- /**
- * Sets the output of this action to indicate if the latest tag was published
- * @param isLatest
- */
- export function outputLatest(isLatest: boolean) {
- core.setOutput('latest', isLatest.toString());
+ return {
+ ref,
+ latest: publishLatest,
+ };
}
}
diff --git a/src/functions/types.ts b/src/functions/types.ts
new file mode 100644
index 0000000..5d779ec
--- /dev/null
+++ b/src/functions/types.ts
@@ -0,0 +1,34 @@
+export type GitHub = ReturnType;
+
+export interface GraphQlQueryRepository {
+ refs: {
+ refsList: {
+ ref: {
+ name: string;
+ object: {
+ shaId: string;
+ };
+ };
+ }[];
+ pageInfo: {
+ endCursor: string;
+ hasNextPage: boolean;
+ };
+ totalCount: number;
+ };
+}
+
+export interface TaggedRef {
+ ref: Ref['name'];
+ latest: boolean;
+}
+
+export interface LatestRef {
+ repoLatest: Ref;
+ majorLatest: Ref;
+}
+
+export interface Ref {
+ name: string;
+ shaId: string;
+}
diff --git a/src/index.ts b/src/index.ts
index ae8516f..533dee7 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,57 +1,6 @@
-export interface TaggedRelease {
- ref: string;
- latest: boolean;
-}
+import * as core from '@actions/core';
+import main from '@actionstagger/main';
-export interface LatestRelease {
- repoLatest: string;
- majorLatest: string;
-}
-
-export interface ActionPreferences {
- readonly publishLatestTag: boolean;
- readonly preferBranchRelease: boolean;
-}
-
-export const preferences: ActionPreferences = {
- publishLatestTag:
- (
- process.env.INPUT_PUBLISH_LATEST ?? process.env.INPUT_PUBLISH_LATEST_TAG
- ).toLowerCase() === 'true',
- preferBranchRelease:
- process.env.INPUT_PREFER_BRANCH_RELEASES.toLowerCase() === 'true',
-};
-
-export interface GraphQlQueryRepository {
- refs: {
- refsList: {
- ref: {
- name: string;
- };
- }[];
- pageInfo: {
- endCursor: string;
- hasNextPage: boolean;
- };
- totalCount: number;
- };
-}
-
-// Test the query at: https://docs.github.com/en/graphql/overview/explorer
-export const queryAllRefs = `
-query ($repoOwner: String!, $repoName: String!, $majorRef: String!, $pagination: String = "") {
- repository(name: $repoName, owner: $repoOwner) {
- refs(refPrefix: $majorRef, first: 100, after: $pagination, orderBy: {field: ALPHABETICAL, direction: DESC}) {
- refsList: edges {
- ref: node {
- name
- }
- }
- pageInfo {
- endCursor
- hasNextPage
- }
- totalCount
- }
- }
-}`;
+main().catch((error: Error) => {
+ core.setFailed(error.message);
+});
diff --git a/src/main.ts b/src/main.ts
index eb5d299..283927c 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,62 +1,46 @@
import * as core from '@actions/core';
import * as github from '@actions/github';
import semverGte from 'semver/functions/gte';
-import { preferences } from '@actionstagger';
+import { preferences } from '@actionstagger/util';
+import type { GitHub } from '@actionstagger/functions/types';
import {
isEditedRelease,
isPublishedRelease,
- isSemVersionedRelease,
- findLatestReleases,
+ findLatestRef,
createRequiredRefs,
- outputLatest,
- outputTagName,
- releaseTag,
+ getPublishRefVersion,
+ isRefPush,
+ isSemVersionedTag,
} from '@actionstagger/functions/barrel';
-run().catch((error: Error) => {
- core.setFailed(error.message);
-});
-
-async function run(): Promise {
- if (!(isPublishedRelease() || isEditedRelease())) {
- core.info('This action should only be used in a release context');
+export default async function main(): Promise {
+ if (!(isRefPush() || isPublishedRelease() || isEditedRelease())) {
+ core.info(
+ 'This action should only be used in a release context or when creating a new tag or branch'
+ );
ifErrorSubmitBug();
return;
}
- if (!isSemVersionedRelease()) {
- core.info('This action can only operate on semantically versioned releases');
+ if (!isSemVersionedTag()) {
+ core.info('This action can only operate on semantically versioned tags');
core.info('See: https://semver.org/');
ifErrorSubmitBug();
return;
}
- if (process.env.GITHUB_TOKEN) {
- // TODO: Deprecate: v3
- core.info(
- `Using obsolete GITHUB_TOKEN environment variable: Please use token
- |arg instead. In most cases the default value will just work and you can
- |simply remove the token variable from your configuration.`.replace(
- /^\s*\|/gm,
- ''
- )
- );
- }
-
- const token = process.env.GITHUB_TOKEN ?? core.getInput('token');
- const octokit = github.getOctokit(token);
- const { repoLatest, majorLatest } = await findLatestReleases(octokit);
+ const octokit = createOctoKit();
+ const { repoLatest, majorLatest } = await findLatestRef(octokit);
+ const releaseVer = getPublishRefVersion();
- const releaseVer = releaseTag();
-
- if (semverGte(releaseVer, majorLatest)) {
+ if (semverGte(releaseVer, majorLatest.name)) {
const overridePubLatest =
- preferences.publishLatestTag && semverGte(releaseVer, repoLatest);
+ preferences.publishLatest && semverGte(releaseVer, repoLatest.name);
const { ref, latest } = await createRequiredRefs(octokit, overridePubLatest);
outputTagName(ref);
outputLatest(latest);
- } else {
+ } else if (majorLatest.shaId !== process.env.GITHUB_SHA) {
core.info(
'Nothing to do because release commit is earlier than major tag commit'
);
@@ -64,12 +48,48 @@ async function run(): Promise {
}
}
+/**
+ * Sets the output of this action to indicate the version that was published/updated
+ * @param refName The tag version
+ */
+function outputTagName(refName: string) {
+ core.setOutput('tag', refName);
+ // TODO: Deprecate: v3
+ core.setOutput('ref_name', refName);
+}
+
+/**
+ * Sets the output of this action to indicate if the latest tag was published
+ * @param isLatest
+ */
+function outputLatest(isLatest: boolean) {
+ core.setOutput('latest', isLatest.toString());
+}
+
+function createOctoKit(): GitHub {
+ let token = core.getInput('token');
+ if (!token && process.env.GITHUB_TOKEN) {
+ // TODO: Deprecate: v3
+ core.info(
+ `Using obsolete GITHUB_TOKEN environment variable: Please use token
+ |arg instead. In most cases the default value will just work and you can
+ |simply remove the token variable from your configuration.`.replace(
+ /^\s*\|/gm,
+ ''
+ )
+ );
+ token = process.env.GITHUB_TOKEN;
+ }
+ // else...
+ return github.getOctokit(token);
+}
+
function ifErrorSubmitBug() {
core.info('If you believe this to be an error, please submit a bug report');
- core.info('https://github.com/Actions-R-Us/actions-tagger/issues');
+ core.info(`https://github.com/${process.env.GITHUB_ACTION_REPOSITORY}/issues`);
if (core.isDebug()) {
core.debug(`event: ${process.env.GITHUB_EVENT_NAME}`);
- core.debug(`tag_name: ${releaseTag().version}`);
+ core.debug(`ref_name: ${getPublishRefVersion()?.raw}`);
}
}
diff --git a/src/util.ts b/src/util.ts
new file mode 100644
index 0000000..9f228dc
--- /dev/null
+++ b/src/util.ts
@@ -0,0 +1,35 @@
+interface ActionPreferences {
+ readonly publishLatest: boolean;
+ readonly preferBranchRelease: boolean;
+}
+
+export const preferences: ActionPreferences = {
+ publishLatest:
+ (
+ process.env.INPUT_PUBLISH_LATEST ?? process.env.INPUT_PUBLISH_LATEST_TAG
+ ).toLowerCase() === 'true',
+ preferBranchRelease:
+ process.env.INPUT_PREFER_BRANCH_RELEASES.toLowerCase() === 'true',
+};
+
+// Test the query at: https://docs.github.com/en/graphql/overview/explorer
+export const queryAllRefs = `
+query ($repoOwner: String!, $repoName: String!, $majorRef: String!, $pagination: String = "") {
+ repository(name: $repoName, owner: $repoOwner) {
+ refs(refPrefix: $majorRef, first: 100, after: $pagination, orderBy: {field: ALPHABETICAL, direction: DESC}) {
+ refsList: edges {
+ ref: node {
+ name
+ object: target {
+ shaId: oid
+ }
+ }
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ totalCount
+ }
+ }
+}`;
diff --git a/tests/functions.test.ts b/tests/functions.test.ts
index cc14218..dc39905 100644
--- a/tests/functions.test.ts
+++ b/tests/functions.test.ts
@@ -1,4 +1,5 @@
import { getOctokit } from '@actions/github';
+import type { GraphQlQueryRepository } from '@actionstagger/functions/types';
beforeEach(() => jest.resetModules());
afterEach(() => jest.restoreAllMocks());
@@ -19,7 +20,7 @@ describe('getPreferredRef()', () => {
});
});
-describe('findLatestReleases()', () => {
+describe('findLatestRef()', () => {
beforeEach(() => {
process.env.GITHUB_REPOSITORY = 'test/test';
});
@@ -28,16 +29,19 @@ describe('findLatestReleases()', () => {
it('Should find the latest release when only one release exists', async () => {
const currentTag = '3.0.1';
- // YES, this require only works in this scope. Don't ask me why, ask the JS/Jest gods
+ // YES, this 'require' only works in this scope. Don't ask me why, ask the JS/Jest gods
const semverTag = require('semver/functions/parse')(`v${currentTag}`);
const spyOctokit = jest.spyOn(octokit, 'graphql').mockImplementation(async () =>
- Promise.resolve({
+ Promise.resolve<{ repository: GraphQlQueryRepository }>({
repository: {
refs: {
refsList: [
{
ref: {
name: `v${currentTag}`,
+ object: {
+ shaId: 'test',
+ },
},
},
],
@@ -55,7 +59,7 @@ describe('findLatestReleases()', () => {
const MockFunctions = jest.requireActual<
typeof import('@actionstagger/functions')
>('@actionstagger/functions').default;
- MockFunctions.releaseTag = jest.fn();
+ MockFunctions.getPublishRefVersion = jest.fn();
return {
__esModule: true,
default: MockFunctions,
@@ -63,12 +67,12 @@ describe('findLatestReleases()', () => {
});
await import('@actionstagger/functions').then(
- async ({ default: { findLatestReleases, releaseTag } }) => {
+ async ({ default: { findLatestRef, getPublishRefVersion } }) => {
// @ts-ignore
- releaseTag.mockReturnValue(semverTag);
- await findLatestReleases(octokit).then(({ repoLatest }) => {
+ getPublishRefVersion.mockReturnValue(semverTag);
+ await findLatestRef(octokit).then(({ repoLatest }) => {
expect(spyOctokit).toHaveBeenCalledTimes(1);
- expect(repoLatest).toBe(currentTag);
+ expect(repoLatest.name).toBe(currentTag);
});
}
);
diff --git a/tests/action.test.ts b/tests/main.test.ts
similarity index 100%
rename from tests/action.test.ts
rename to tests/main.test.ts
diff --git a/tests/test-setup.ts b/tests/test-setup.ts
index c9b8200..a25c913 100644
--- a/tests/test-setup.ts
+++ b/tests/test-setup.ts
@@ -1,6 +1,8 @@
+// https://docs.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables
export default async () => {
process.env.INPUT_PUBLISH_LATEST_TAG = 'false';
process.env.INPUT_PREFER_BRANCH_RELEASES = 'false';
process.env.INPUT_TOKEN = 'test-token';
process.env.GITHUB_REPOSITORY = 'test/test';
+ process.env.GITHUB_ACTION_REPOSITORY = 'Actions-R-Us/actions-tagger';
};
diff --git a/tests/preferences.test.ts b/tests/util.test.ts
similarity index 54%
rename from tests/preferences.test.ts
rename to tests/util.test.ts
index 44ea39a..fa87f68 100644
--- a/tests/preferences.test.ts
+++ b/tests/util.test.ts
@@ -1,35 +1,35 @@
beforeEach(() => jest.resetModules());
describe('preferences.publishLatestTag', () => {
- it('Should have been set to false', () =>
- import('@actionstagger').then(({ preferences }) =>
- expect(preferences.publishLatestTag).toBe(false)
+ it('Should have been set to false', async () =>
+ await import('@actionstagger/util').then(({ preferences }) =>
+ expect(preferences.publishLatest).toBe(false)
));
it('Should have been set to true when deprecated input is true', async () => {
process.env.INPUT_PUBLISH_LATEST = 'true';
- return import('@actionstagger').then(({ preferences }) =>
- expect(preferences.publishLatestTag).toBe(true)
+ await import('@actionstagger/util').then(({ preferences }) =>
+ expect(preferences.publishLatest).toBe(true)
);
});
it('Should have been set to true when input is true', async () => {
process.env.INPUT_PUBLISH_LATEST_TAG = 'true';
- return import('@actionstagger').then(({ preferences }) =>
- expect(preferences.publishLatestTag).toBe(true)
+ await import('@actionstagger/util').then(({ preferences }) =>
+ expect(preferences.publishLatest).toBe(true)
);
});
});
describe('preferences.preferBranchRelease', () => {
- it('Should have been set to false', () =>
- import('@actionstagger').then(({ preferences }) =>
+ it('Should have been set to false', async () =>
+ await import('@actionstagger/util').then(({ preferences }) =>
expect(preferences.preferBranchRelease).toBe(false)
));
it('Should have been set to true when input is true', async () => {
process.env.INPUT_PREFER_BRANCH_RELEASES = 'true';
- return import('@actionstagger').then(({ preferences }) =>
+ await import('@actionstagger/util').then(({ preferences }) =>
expect(preferences.preferBranchRelease).toBe(true)
);
});